Given the following classes (factories used because C# doesn't support type inference on constructors):
public class A<T>
{
public A(B<T> b) { }
}
public class B<T>
{
public B(C<T> c) { }
}
public class C<T>
{
public C(T tee) { }
}
public class D<T>
{
public static D<T> Create<V>(Expression<Func<T, V>> property)
{
return new D<T, V>(property);
}
}
public class D<T, V> : D<T>
{
public D(Expression<Func<T, V>> property) { }
}
public class Model
{
public int P1 { get; set; }
public string P2 { get; set; }
}
public class AFactory
{
public static A<T> Create<T>(B<T> bee)
{
return new A<T>(bee);
}
}
public class BFactory
{
public static B<T> Create<T>(C<T> cee)
{
return new B<T>(cee);
}
}
public class CFactory
{
public static C<T> Create<T>(params D<T>[] tees)
{
return null;
}
}
The following compiles:
AFactory.Create(BFactory.Create(CFactory.Create(
D<Model>.Create(m => m.P1), D<Model>.Create(m => m.P2)
)));
The following does not:
AFactory.Create<Model>(BFactory.Create(CFactory.Create(
D.Create(m => m.P1), D.Create(m => m.P2)
)));
The difference is that in the first example I'm specifying the type of the model on the innermost classes, so type inference works normally and propagates up the tree. The problem is that I then have to specify the model type on every D.Create() call, which seems redundant.
The second example is the way I'd like to write this code: tell the outermost class that its type is Model and all the classes that are being constructed use that type as well. Essentially, it's syntactic sugar for AFactory.Create<Model>(BFactory.Create<Model>(/* turtles all the way down... */)).
Is there any way to achieve this in C#? I've tried all the permutations of inheritance and type constraints that I can think of, but nothing has given me the desired result.
I'm also fully aware that I might be missing something fundamental about generics - please feel free to educate me if that's the case.
Related
I am trying to create something very similar to how EF Core uses builders in their entity configuration (e.g., IEntityTypeConfiguration<TEntity>, EntityTypeBuilder<TEntity>, PropertyBuilder<TEntity>).
In the below code, I would like the ObjectBuilder<> class and SubObject() method to infer the type from the class they are implemented in (in this case, MyClass). Is this possible?
It would also be great if I could infer the second type from the interface being implemented (the SomeType type), but I am pretty sure this is not possible.
It may be that I need a completely different approach to this, but I have yet to come up with an alternate solution.
Thank you for the help.
public interface ISomeInterface<T2> { }
public class SomeType
{
public string MySubObjectField { get; }
public string MySubObjectField2 { get; }
}
public class MyClass : ISomeInterface<SomeType>
{
public string MyObjectField { get; }
public string MyObjectField2 { get; }
public MyClass()
{
ObjectBuilder<MyClass, SomeType> builder = new ObjectBuilder<MyClass, SomeType>();
builder
.SubObject<MyClass>(c => c.MyObjectField)
.HasSomething(t => t.MySubObjectField)
.IsSomething();
builder
.SubObject<MyClass>(c => c.MyObjectField2)
.HasSomething(t => t.MySubObjectField2);
}
}
public class ObjectBuilder<T1, T2>
{
public SubObjectBuilder<T2> SubObject<T1>(Expression<Func<T1, object>> expression)
{
// Simplified for question
return new SubObjectBuilder<T2>();
}
}
public class SubObjectBuilder<T2>
{
public SubObjectBuilder<T2> HasSomething(Expression<Func<T2, object>> expression)
{
// Do something
return this;
}
public SubObjectBuilder<T2> IsSomething()
{
// Do something
return this;
}
}
lately I started to learn generics. I run into trouble with storing references to generic classes instances. As you can see, my class ListHandler can store references to specific type of BaseClass. I would love to register BaseClass instances by themselves, which is why I wanted to guarantee that they will use BaseParamClass by adding 'where'. Anyway - it does not compile.'This', does not know that T is actually BaseClassParam even with 'where' keyword in class. I don't know what is wrong here and I couldn't find answer anywhere. I would be grateful for tips/guides/solutions.
public class ListHandler
{
private List<BaseClass<BaseParamClass>> list;
public ListHandler()
{
list = new List<BaseClass<BaseParamClass>>();
}
public void Register(BaseClass<BaseParamClass> param)
{
list.Add(param);
}
}
public class BaseClass<T> where T : BaseParamClass
{
private ListHandler listHandler;
public T Param { get; private set; }
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this); //throws error
}
}
Why don't you make ListHandler generic as well?
public class ListHandler<T>
{
private List<BaseClass<T>> list;
public ListHandler()
{
list = new List<BaseClass<T>>();
}
public void Register(BaseClass<T> param)
{
list.Add(param);
}
}
public class BaseClass<T>
{
private ListHandler<T> listHandler;
public T Param { get; private set; }
public BaseClass(ListHandler<T> listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this);
}
}
Also, it seems strange to me to have BaseClass<T> contain a reference to a class that has a reference to BaseClass<T> itself.
I have another option for you.
Let's split the BaseClass<T> class into two with a non-generic base, like so:
public class BaseClass
{
protected ListHandler listHandler;
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
}
}
public class BaseClass<T> : BaseClass where T : BaseParamClass
{
public T Param { get; private set; }
public BaseClass(ListHandler listHandler)
: base(listHandler)
{
listHandler.Register(this); // Compiles nicely! Yay!
}
}
Now, the list inside ListHandler can be defined as private List<BaseClass> list;. That means there is no problem adding any BaseClass item to the list. We also can then define two methods for registering and fetching generic versions of the BaseClass<T> from the ListHandler. It would look like this:
public class ListHandler
{
private List<BaseClass> list;
public ListHandler()
{
list = new List<BaseClass>();
}
public void Register<T>(BaseClass<T> param) where T : BaseParamClass
{
list.Add(param);
}
public BaseClass<T> Fetch<T>() where T : BaseParamClass
{
return list.Select(x => x as BaseClass<T>).Where(x => x != null).FirstOrDefault();
}
}
So, given a class public class FooParam : BaseParamClass { } I can write this code:
ListHandler listHandler = new ListHandler();
BaseClass<FooParam> baseClass = new BaseClass<FooParam>(listHandler);
BaseClass<FooParam> baseClass2 = listHandler.Fetch<FooParam>();
Console.WriteLine(object.ReferenceEquals(baseClass, baseClass2));
The result from this code is True is written to the console - which means I can successfully fetch the instance of BaseClass<FooParam> from the ListHandler.
Why your code doesn't compile
In order to fully understand why your code doesn't compile, you'll have to dive into covariance and contravariance, which is a big topic and hard to explain in an SO answer. It can be especially confusing if you've gotten to a point where inheritance polymorphism is second nature to you; the rules are just different enough to be make your head hurt.
Here is what is confusing--
You're used to doing this:
object a = new String(...);
But generics don't let you do this!
List<object> c = new List<string>(); //Compiler error
That's because those two Lists are not related the same way that object and string are related. One does not inherit from the other. Rather, they are different variants of a generic type definition. In the generic world, you can't assign one to the other. The same is true of this:
void Foo<T>() where T: BaseParamClass
{
BaseClass<BaseParamClass> a = new BaseClass<T>(); //Compiler error
}
In this example, T could be BaseParamClass or one of its derived types. They are not the same type. So to remain type-safe, the compiler has to disallow this assignment, and your Register call, which has the same type mismatch.
Standard ways around this
You need a covariant interface. These allow assignment from derived to base. So for example, while this is still illegal:
List<object> a = new List<string>(); //Compiler error
This is totally fine:
IEnumerable<object> e = new List<string>(); //Is OK
Because IEnumerable was declared to be covariant, like this:
interface IEnumerable<out T>
Which means it is can be assigned in this way. It works because using out also adds a compiler constraint to the interface: it can be used to retrieve stuff...
interface IEnumerable<out T>
{
T Item[int index];
}
...but it cannot accept anything:
interface IEnumerable<out T>
{
Add(T item); //Compiler error
}
These constraints are what allow generics to provide early-bound type safety while still allowing certain forms of (non-inheritance) polymorphism.
What I'd suggest
Based on your comment, it sounds like you just need a container (a stack, apparently) that can hold references to these BaseClass<T> instances. If you are following separation of concerns, the stack doesn't need to actually do anything with the T, other than store it and retrieve it, and to allow it to register itself.
Since that is a separate concern, make a separate interface.
And in the interest of keeping things simple, maybe avoid using generics completely for this bit.
One way to do it--
Create an interface that allows access to everything the stack needs to know about an item it is containing. For example, if the stack contains popups of various kinds, you may want to expose the popup's title.
interface IStackable
{
string Title { get; set; }
}
Now use it like this:
public class ListHandler
{
private readonly Dictionary<string, IStackable> list;
public ListHandler()
{
list = new Dictionary<string, IStackable>();
}
public void Register(IStackable item)
{
list.Add(item.Title, item);
}
}
public class BaseClass<T> : IStackable where T : BaseParamClass
{
private ListHandler listHandler;
public T Param { get; private set; }
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this);
}
public string Title { get; set; }
}
Unless there is some other requirement, you shouldn't need to make it any more complicated than that.
All you really need to do is add an interface. This works:
public class BaseParamClass
{
}
public class ListHandler
{
private List<IBase<BaseParamClass>> list;
public ListHandler()
{
list = new List<IBase<BaseParamClass>>();
}
public void Register(IBase<BaseParamClass> param)
{
list.Add(param);
}
}
public interface IBase<T> where T : BaseParamClass
{
T Param {get; }
}
public class BaseClass : IBase<BaseParamClass>
{
private ListHandler listHandler;
public BaseParamClass Param { get; private set; }
public BaseClass(ListHandler listHandler)
{
this.listHandler = listHandler;
listHandler.Register(this);
}
}
Working code on DotNetFiddle
I have the following classes
public abstract class BaseViewPresenter { }
public abstract class BaseView<T> : UserControl
where T : BaseViewPresenter { }
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter> { }
I have a method that looks like this (simplified)
public BaseView<BaseViewPresenter> Resolve(BaseViewPresenter model)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
// Correctly creates BaseView object
var control = Activator.CreateInstance(viewType);
// Fails to cast as BaseView<BaseViewPresenter> so returns null
return control as BaseView<BaseViewPresenter>;
}
When I call this using an instances of LoginPresenter
var login = new LoginPresenter();
var ctl = Resolve(login);
The line Activator.CreateInstance(viewType) correctly resolves into a new instances of my LoginView, however control as BaseView<BaseViewPresenter> can't do the cast correctly so returns null.
Is there a way to correctly cast the control into BaseView<BaseViewPresenter> without using specific type generics?
Since LoginView inherits from BaseView<LoginPresenter>, and LoginPresenter inherits from BaseViewPresenter, I would assume there's a way to convert LoginView to BaseView<BaseViewPresenter>.
I am stuck with using .Net 3.5
This is a very frequently asked question. Let's rename your types:
abstract class Fruit { } // was BaseViewPresenter
abstract class FruitBowl<T> where T : Fruit // was BaseView
class Apple : Fruit { } // was LoginPresenter
class BowlOfApples : FruitBowl<Apple> { } // was LoginView
Your question now is:
I have a BowlOfApples, which inherits from FruitBowl<Apple>. Why can I not use it as a FruitBowl<Fruit>? An apple is a fruit, so a bowl of apples is a bowl of fruit.
No, it isn't. You can put a banana in a bowl of fruit, but you can't put a banana in a bowl of apples, and therefore a bowl of apples is not a bowl of fruit. (And by similar argument, a bowl of fruit is not a bowl of apples either.) Since the operations you can legally perform on the two types are different, they cannot be compatible.
Here is a photo of StackOverflow legend Jon Skeet demonstrating this fact:
The feature you want is called generic contravariance, and it is supported only on interfaces and delegate types when the compiler can prove that the variance is safe, and when the varying type is a reference type. For example, you can use an IEnumerable<Apple> in a context where IEnumerable<Fruit> is needed because the compiler can verify that there is no way that you can put a Banana into a sequence of fruit.
Do a search on "C# covariance and contravariance" on this site or on the web and you'll find many more details about how this feature works. In particular, my series of articles on how we designed and implemented this feature in C# 4 starts here: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx
I accepted Eric's answer since it provides a great explanation of why what I wanted wasn't possible, but I also thought I'd share my solution in case anyone else runs into this same problem.
I removed the generic type parameter from my original BaseView class, and created a 2nd version of the BaseView class that included the generic type parameter and specifics for it.
The first version is used by my .Resolve() method or other code that doesn't care about the specific types, and the second version is used by any code that does care, such as the implentation of a BaseView
Here's an example of how my code ended up looking
// base classes
public abstract class BaseViewPresenter { }
public abstract class BaseView : UserControl
{
public BaseViewPresenter Presenter { get; set; }
}
public abstract class BaseView<T> : BaseView
where T : BaseViewPresenter
{
public new T Presenter
{
get { return base.Presenter as T; }
set { base.Presenter = value; }
}
}
// specific classes
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter>
{
// Can now call things like Presenter.LoginPresenterMethod()
}
// updated .Resolve method used for obtaining UI object
public BaseView Resolve(BaseViewPresenter presenter)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
BaseView view = Activator.CreateInstance(viewType) as BaseView;
view.Presenter = presenter;
return view;
}
You're expecting to treat the type as being covariant with respect to the generic argument. Classes can never be covariant; you'd need to use an interface rather than (or in addition to) an abstract class to make it covariant with respect to T. You'd also need to be using C# 4.0.
My usual solution to this problem is to create an intermediary class that has access to the type-parametric class's methods through delegates. Fields can also be accessed through getters/setters.
The general pattern goes:
public abstract class Super {}
public abstract class MyAbstractType<T> where T : Super {
public MyGeneralType AsGeneralType() {
return MyGeneralType.Create(this);
}
// Depending on the context, an implicit cast operator might make things
// look nicer, though it might be too subtle to some tastes.
public static implicit operator MyGeneralType(MyAbstractType<T> t) {
return MyGeneralType.Create(t);
}
public int field;
public void MyMethod1() {}
public void MyMethod2(int argument) {}
public abstract bool MyMethod3(string argument);
}
public delegate T Getter<T>();
public delegate void Setter<T>(T value);
public delegate void MyMethod1Del();
public delegate void MyMethod2Del(int argument);
public delegate bool MyMethod3Del(string argument);
public class MyGeneralType {
public Getter<int> FieldGetter;
public Setter<int> FieldSetter;
public MyMethod1Del MyMethod1;
public MyMethod2Del MyMethod2;
public MyMethod3Del MyMethod3;
public static MyGeneralType Create<T>(MyAbstractType<T> t) where T : Super {
var g = new MyGeneralType();
g.FieldGetter = delegate { return t.field; };
g.FieldSetter = value => { t.field = value; };
g.MyMethod1 = t.MyMethod1;
g.MyMethod2 = t.MyMethod2;
g.MyMethod3 = t.MyMethod3;
return g;
}
public int field {
get { return FieldGetter(); }
set { FieldSetter(value); }
}
}
The above exemplifies getting all the methods and fields but normally I only need a few of them. This is a general solution to the problem and one could feasibly write a tool to generate these intermediary classes automatically, which I might at some point.
Try it here: https://dotnetfiddle.net/tLkmgR
Note that this is enough for all my cases, but you can be extra hacky with this:
public abstract class MyAbstractType<T> where T : Super {
// ... Same everything else ...
// data fields must become abstract getters/setters, unfortunate
public abstract int field {
get;
set;
}
public static implicit operator MyAbstractType<Super>(MyAbstractType<T> t) {
return MyGeneralType.Create(t);
}
}
public class MyGeneralType : MyAbstractType<Super> {
// ... same constructors and setter/getter
// fields but only keep method fields
// that contain the method references for
// implementations of abstract classes,
// and rename them not to clash with the
// actual method names ...
public MyMethod3Del myMethod3Ref;
// Implement abstract methods by calling the corresponding
// method references.
public override bool MyMethod3(string argument) {
return myMethod3Ref(argument);
}
// Same getters/setters but with override keyword
public override int field {
get { return FieldGetter(); }
set { FieldSetter(value); }
}
}
And there you go, now you can literally cast a MyAbstractType<Sub> where Sub : Super to a MyAbstractType<Super>, although it's no longer the same object anymore, but it does retain the same methods and data, it's sort of a complex pointer.
public class Sub : Super {}
public class MySubType : MyAbstractType<Sub> {
public int _field;
public override int field {
get { return _field; }
set { _field = value; }
}
public override bool MyMethod3(string argument) {
Console.WriteLine("hello " + argument);
return argument == "world";
}
}
public class MainClass {
public static void Main() {
MyAbstractType<Sub> sub = new MyAbstractType<Sub>();
MyAbstractType<Super> super = sub;
super.MyMethod3("hello"); // calls sub.MyMethod3();
super.field = 10; // sets sub.field
}
}
This isn't as good in my opinion, the other version of MyGeneralType is a more straighforward layer over the concrete types, plus it doesn't require rewriting the data fields, but it does actually answer the question, technically. Try it here: https://dotnetfiddle.net/S3r3ke
Example
Using these abstract classes:
public abstract class Animal {
public string name;
public Animal(string name) {
this.name = name;
}
public abstract string Sound();
}
public abstract class AnimalHouse<T> where T : Animal {
List<T> animals;
public AnimalHouse(T[] animals) {
this.animals = animals.ToList();
}
public static implicit operator GeneralAnimalHouse(AnimalHouse<T> house) {
return GeneralAnimalHouse.Create(house);
}
public List<string> HouseSounds() {
return animals.Select(animal => animal.Sound()).ToList();
}
}
We make this "general" variant:
public delegate List<string> HouseSoundsDel();
public class GeneralAnimalHouse {
public HouseSoundsDel HouseSounds;
public static GeneralAnimalHouse Create<T>(AnimalHouse<T> house) where T : Animal {
var general = new GeneralAnimalHouse();
general.HouseSounds = house.HouseSounds;
return general;
}
}
And finally with these inheritors:
public class Dog : Animal {
public Dog(string name) : base(name) {}
public override string Sound() {
return name + ": woof";
}
}
public class Cat : Animal {
public Cat(string name) : base(name) {}
public override string Sound() {
return name + ": meow";
}
}
public class DogHouse : AnimalHouse<Dog> {
public DogHouse(params Dog[] dogs) : base(dogs) {}
}
public class CatHouse : AnimalHouse<Cat> {
public CatHouse(params Cat[] cats) : base(cats) {}
}
We use it like this:
public class AnimalCity {
List<GeneralAnimalHouse> houses;
public AnimalCity(params GeneralAnimalHouse[] houses) {
this.houses = houses.ToList();
}
public List<string> CitySounds() {
var random = new Random();
return houses.SelectMany(house => house.HouseSounds())
.OrderBy(x => random.Next())
.ToList();
}
}
public class MainClass {
public static void Main() {
var fluffy = new Cat("Fluffy");
var miu = new Cat("Miu");
var snuffles = new Cat("Snuffles");
var snoopy = new Dog("Snoopy");
var marley = new Dog("Marley");
var megan = new Dog("Megan");
var catHouse = new CatHouse(fluffy, miu, snuffles);
var dogHouse = new DogHouse(snoopy, marley, megan);
var animalCity = new AnimalCity(catHouse, dogHouse);
foreach (var sound in animalCity.CitySounds()) {
Console.WriteLine(sound);
}
}
}
Output:
Miu: meow
Snoopy: woof
Snuffles: meow
Fluffy: meow
Marley: woof
Megan: woof
Notes:
I added names so it's clear that the method references carry their owner's data with them, for those unfamiliar with delegates.
The required using statements for this code are System, System.Collections.Generic, and System.Linq.
You can try it here: https://dotnetfiddle.net/6qkHL3#
A version that makes GeneralAnimalHouse a subclass of AnimalHouse<Animal> can be found here: https://dotnetfiddle.net/XS0ljg
I have the following classes
public abstract class BaseViewPresenter { }
public abstract class BaseView<T> : UserControl
where T : BaseViewPresenter { }
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter> { }
I have a method that looks like this (simplified)
public BaseView<BaseViewPresenter> Resolve(BaseViewPresenter model)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
// Correctly creates BaseView object
var control = Activator.CreateInstance(viewType);
// Fails to cast as BaseView<BaseViewPresenter> so returns null
return control as BaseView<BaseViewPresenter>;
}
When I call this using an instances of LoginPresenter
var login = new LoginPresenter();
var ctl = Resolve(login);
The line Activator.CreateInstance(viewType) correctly resolves into a new instances of my LoginView, however control as BaseView<BaseViewPresenter> can't do the cast correctly so returns null.
Is there a way to correctly cast the control into BaseView<BaseViewPresenter> without using specific type generics?
Since LoginView inherits from BaseView<LoginPresenter>, and LoginPresenter inherits from BaseViewPresenter, I would assume there's a way to convert LoginView to BaseView<BaseViewPresenter>.
I am stuck with using .Net 3.5
This is a very frequently asked question. Let's rename your types:
abstract class Fruit { } // was BaseViewPresenter
abstract class FruitBowl<T> where T : Fruit // was BaseView
class Apple : Fruit { } // was LoginPresenter
class BowlOfApples : FruitBowl<Apple> { } // was LoginView
Your question now is:
I have a BowlOfApples, which inherits from FruitBowl<Apple>. Why can I not use it as a FruitBowl<Fruit>? An apple is a fruit, so a bowl of apples is a bowl of fruit.
No, it isn't. You can put a banana in a bowl of fruit, but you can't put a banana in a bowl of apples, and therefore a bowl of apples is not a bowl of fruit. (And by similar argument, a bowl of fruit is not a bowl of apples either.) Since the operations you can legally perform on the two types are different, they cannot be compatible.
Here is a photo of StackOverflow legend Jon Skeet demonstrating this fact:
The feature you want is called generic contravariance, and it is supported only on interfaces and delegate types when the compiler can prove that the variance is safe, and when the varying type is a reference type. For example, you can use an IEnumerable<Apple> in a context where IEnumerable<Fruit> is needed because the compiler can verify that there is no way that you can put a Banana into a sequence of fruit.
Do a search on "C# covariance and contravariance" on this site or on the web and you'll find many more details about how this feature works. In particular, my series of articles on how we designed and implemented this feature in C# 4 starts here: http://blogs.msdn.com/b/ericlippert/archive/2007/10/16/covariance-and-contravariance-in-c-part-one.aspx
I accepted Eric's answer since it provides a great explanation of why what I wanted wasn't possible, but I also thought I'd share my solution in case anyone else runs into this same problem.
I removed the generic type parameter from my original BaseView class, and created a 2nd version of the BaseView class that included the generic type parameter and specifics for it.
The first version is used by my .Resolve() method or other code that doesn't care about the specific types, and the second version is used by any code that does care, such as the implentation of a BaseView
Here's an example of how my code ended up looking
// base classes
public abstract class BaseViewPresenter { }
public abstract class BaseView : UserControl
{
public BaseViewPresenter Presenter { get; set; }
}
public abstract class BaseView<T> : BaseView
where T : BaseViewPresenter
{
public new T Presenter
{
get { return base.Presenter as T; }
set { base.Presenter = value; }
}
}
// specific classes
public class LoginPresenter : BaseViewPresenter { }
public partial class LoginView : BaseView<LoginPresenter>
{
// Can now call things like Presenter.LoginPresenterMethod()
}
// updated .Resolve method used for obtaining UI object
public BaseView Resolve(BaseViewPresenter presenter)
{
var type = model.GetType();
var viewType = _dataTemplates[type];
BaseView view = Activator.CreateInstance(viewType) as BaseView;
view.Presenter = presenter;
return view;
}
You're expecting to treat the type as being covariant with respect to the generic argument. Classes can never be covariant; you'd need to use an interface rather than (or in addition to) an abstract class to make it covariant with respect to T. You'd also need to be using C# 4.0.
My usual solution to this problem is to create an intermediary class that has access to the type-parametric class's methods through delegates. Fields can also be accessed through getters/setters.
The general pattern goes:
public abstract class Super {}
public abstract class MyAbstractType<T> where T : Super {
public MyGeneralType AsGeneralType() {
return MyGeneralType.Create(this);
}
// Depending on the context, an implicit cast operator might make things
// look nicer, though it might be too subtle to some tastes.
public static implicit operator MyGeneralType(MyAbstractType<T> t) {
return MyGeneralType.Create(t);
}
public int field;
public void MyMethod1() {}
public void MyMethod2(int argument) {}
public abstract bool MyMethod3(string argument);
}
public delegate T Getter<T>();
public delegate void Setter<T>(T value);
public delegate void MyMethod1Del();
public delegate void MyMethod2Del(int argument);
public delegate bool MyMethod3Del(string argument);
public class MyGeneralType {
public Getter<int> FieldGetter;
public Setter<int> FieldSetter;
public MyMethod1Del MyMethod1;
public MyMethod2Del MyMethod2;
public MyMethod3Del MyMethod3;
public static MyGeneralType Create<T>(MyAbstractType<T> t) where T : Super {
var g = new MyGeneralType();
g.FieldGetter = delegate { return t.field; };
g.FieldSetter = value => { t.field = value; };
g.MyMethod1 = t.MyMethod1;
g.MyMethod2 = t.MyMethod2;
g.MyMethod3 = t.MyMethod3;
return g;
}
public int field {
get { return FieldGetter(); }
set { FieldSetter(value); }
}
}
The above exemplifies getting all the methods and fields but normally I only need a few of them. This is a general solution to the problem and one could feasibly write a tool to generate these intermediary classes automatically, which I might at some point.
Try it here: https://dotnetfiddle.net/tLkmgR
Note that this is enough for all my cases, but you can be extra hacky with this:
public abstract class MyAbstractType<T> where T : Super {
// ... Same everything else ...
// data fields must become abstract getters/setters, unfortunate
public abstract int field {
get;
set;
}
public static implicit operator MyAbstractType<Super>(MyAbstractType<T> t) {
return MyGeneralType.Create(t);
}
}
public class MyGeneralType : MyAbstractType<Super> {
// ... same constructors and setter/getter
// fields but only keep method fields
// that contain the method references for
// implementations of abstract classes,
// and rename them not to clash with the
// actual method names ...
public MyMethod3Del myMethod3Ref;
// Implement abstract methods by calling the corresponding
// method references.
public override bool MyMethod3(string argument) {
return myMethod3Ref(argument);
}
// Same getters/setters but with override keyword
public override int field {
get { return FieldGetter(); }
set { FieldSetter(value); }
}
}
And there you go, now you can literally cast a MyAbstractType<Sub> where Sub : Super to a MyAbstractType<Super>, although it's no longer the same object anymore, but it does retain the same methods and data, it's sort of a complex pointer.
public class Sub : Super {}
public class MySubType : MyAbstractType<Sub> {
public int _field;
public override int field {
get { return _field; }
set { _field = value; }
}
public override bool MyMethod3(string argument) {
Console.WriteLine("hello " + argument);
return argument == "world";
}
}
public class MainClass {
public static void Main() {
MyAbstractType<Sub> sub = new MyAbstractType<Sub>();
MyAbstractType<Super> super = sub;
super.MyMethod3("hello"); // calls sub.MyMethod3();
super.field = 10; // sets sub.field
}
}
This isn't as good in my opinion, the other version of MyGeneralType is a more straighforward layer over the concrete types, plus it doesn't require rewriting the data fields, but it does actually answer the question, technically. Try it here: https://dotnetfiddle.net/S3r3ke
Example
Using these abstract classes:
public abstract class Animal {
public string name;
public Animal(string name) {
this.name = name;
}
public abstract string Sound();
}
public abstract class AnimalHouse<T> where T : Animal {
List<T> animals;
public AnimalHouse(T[] animals) {
this.animals = animals.ToList();
}
public static implicit operator GeneralAnimalHouse(AnimalHouse<T> house) {
return GeneralAnimalHouse.Create(house);
}
public List<string> HouseSounds() {
return animals.Select(animal => animal.Sound()).ToList();
}
}
We make this "general" variant:
public delegate List<string> HouseSoundsDel();
public class GeneralAnimalHouse {
public HouseSoundsDel HouseSounds;
public static GeneralAnimalHouse Create<T>(AnimalHouse<T> house) where T : Animal {
var general = new GeneralAnimalHouse();
general.HouseSounds = house.HouseSounds;
return general;
}
}
And finally with these inheritors:
public class Dog : Animal {
public Dog(string name) : base(name) {}
public override string Sound() {
return name + ": woof";
}
}
public class Cat : Animal {
public Cat(string name) : base(name) {}
public override string Sound() {
return name + ": meow";
}
}
public class DogHouse : AnimalHouse<Dog> {
public DogHouse(params Dog[] dogs) : base(dogs) {}
}
public class CatHouse : AnimalHouse<Cat> {
public CatHouse(params Cat[] cats) : base(cats) {}
}
We use it like this:
public class AnimalCity {
List<GeneralAnimalHouse> houses;
public AnimalCity(params GeneralAnimalHouse[] houses) {
this.houses = houses.ToList();
}
public List<string> CitySounds() {
var random = new Random();
return houses.SelectMany(house => house.HouseSounds())
.OrderBy(x => random.Next())
.ToList();
}
}
public class MainClass {
public static void Main() {
var fluffy = new Cat("Fluffy");
var miu = new Cat("Miu");
var snuffles = new Cat("Snuffles");
var snoopy = new Dog("Snoopy");
var marley = new Dog("Marley");
var megan = new Dog("Megan");
var catHouse = new CatHouse(fluffy, miu, snuffles);
var dogHouse = new DogHouse(snoopy, marley, megan);
var animalCity = new AnimalCity(catHouse, dogHouse);
foreach (var sound in animalCity.CitySounds()) {
Console.WriteLine(sound);
}
}
}
Output:
Miu: meow
Snoopy: woof
Snuffles: meow
Fluffy: meow
Marley: woof
Megan: woof
Notes:
I added names so it's clear that the method references carry their owner's data with them, for those unfamiliar with delegates.
The required using statements for this code are System, System.Collections.Generic, and System.Linq.
You can try it here: https://dotnetfiddle.net/6qkHL3#
A version that makes GeneralAnimalHouse a subclass of AnimalHouse<Animal> can be found here: https://dotnetfiddle.net/XS0ljg
I have a helper class that does a simple but repetitive process on a List of entities. For simplicity, it's like this...
public static List<MyType> DoSomethingSimple(List<MyType> myTypes) {
return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}
I now need to add support for another type, but everything is identical... how do I avoid an increasing list of overloaded methods like this:
public static List<MyType> DoSomethingSimple(List<MyType> myTypes) {
return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}
public static List<MyOtherType> DoSomethingSimple(List<MyOtherType> myOtherTypes) {
return myOtherTypes.Where(myOtherType => myOtherType.SomeProperty.Equals(2)).ToList();
}
... and so on.
Here's two ways:
Use generics, and a common base class
Use interfaces
Method 1:
public class BaseClass
{
public int SomeProperty { get; set; }
}
public class MyType : BaseClass { }
public class MyOtherType : BaseClass { }
public class ClassWithMethod
{
public static List<T> DoSomethingSimple<T>(List<T> myTypes)
where T : BaseClass
{
return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}
}
Method 2:
public interface ICommon
{
int SomeProperty { get; set; }
}
public class MyType : ICommon
{
public int SomeProperty { get; set; }
}
public class MyOtherType : ICommon
{
public int SomeProperty { get; set; }
}
public class ClassWithMethod
{
public static List<T> DoSomethingSimple<T>(List<T> myTypes)
where T : ICommon
{
return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}
}
Now, if you try to make the method use the interface directly, like this:
public class ClassWithMethod
{
public static List<ICommon> DoSomethingSimple(List<ICommon> myTypes)
{
return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}
}
Then that would work if you have a List<ICommon> when you call it, but won't work if you have a List<MyType>. In C# 4.0 this can be done if we change the method slightly:
public class ClassWithMethod
{
public static List<ICommon> DoSomethingSimple(IEnumerable<ICommon> myTypes)
{
return myTypes.Where(myType => myType.SomeProperty.Equals(2)).ToList();
}
}
Note that I changed to using an IEnumerable<ICommon> instead. The concept here is called Co- and contra-variance, and beyond that I'm not going to say much about it. Search Stack Overflow for more information on the subject.
Tip: I would change the input parameter to be IEnumerable<T> regardless, since this would make your method usable in more instances, you could have different types of collections, arrays, etc. and as long as they contain the right type, they can be passed to the method. By limiting yourself to List<T> you force the user of your code to convert to a list in some cases. My guidelines are to be as unspecific as possible in input parameters, and as specific as possible in output parameters.
Assuming the property has the same name and type for each list type, you could add an interface containing the property and implement it for each type you want to call this method on:
public interface ISomeProperty
{
object SomeProperty { get; }
}
DoSomethingSimple could then be:
public static List<T> DoSomethingSimple<T>(IEnumerable<T> list) where T : ISomeProperty
{
return list.Where(i => i.SomeProperty.Equals(2)).ToList();
}