Using base class as generic for IEnumerable<T> - c#

I have a good understanding of OOP in general, inheritance and polymorphism, interfaces, etc. I encountered a strange situation and I don't understand why it does not work at all...
EDIT : Ok, I found out that covariance (or contravariance?) may solve this problem, but crucially
we're still using .NET 2.0
How can I solve this without moving to C# 4.0 ?
Here is the situation. Given these two classes :
public class CustomCollectionType<T> : IEnumerable<T>
{
/* Implementation here, not really important */
}
public class Entity : EntityBase
{
/* Implentation here, not important */
}
The compiler complains when I try to have this generic method
public void LoopThrough(IEnumerable<EntityBase> entityList)
{
foreach(EntityBase entity in entityList)
{
DoSomething(entity);
}
}
And try to use it this way :
CustomCollectionType<Entity> entityList;
/* Add items to list */
LoopThrough(entityList);
Error says I cannot convert from CustomCollectionType<Entity> to IEnumerable<EntityBase>.
However, I can do this :
public void Foo(EntityBase entity)
{
entity.DoSomething();
}
Foo(new Entity());
And this :
public void Bar(IEnumerable<Entity> entityList)
{ ... }
CustomCollectionType<Entity> entityList;
Bar(entityList);
Why can't I create my method with the highest classes in the hierarchy? The types are obviously compatible... Am I missing something ?
EDIT : I want to solve this problem without altering the existing classes in any way, so creating a new method in any of the classes, or implementing an additional interface is out of the question.

Let's consider your first case. You have:
class Bowl<T> : IEnumerable<T> {}
class Apple : Fruit {}
...
void LoopThrough(IEnumerable<Fruit> fruits) ...
and you call
Bowl<Apple> apples = whatever;
LoopThrough(apples);
This fails in C# 3.0; it succeeds in C# 4.0 because IEnumerable<T> is now covariant in T; a sequence of apples can be used as a sequence of fruits.
To make it work in C# 3.0 you can use the Cast sequence operator.
Bowl<Apple> apples = whatever;
LoopThrough(apples.Cast<Fruit>());
To make it work in C# 2.0, implement the Cast sequence operator yourself. It is only a couple lines of code.
Note that in C# 4.0 it will still not be legal to say:
Bowl<Fruit> fruits = new Bowl<Apples>();
because of course you can say:
fruits.Add(new Orange());
and you just put an orange into a bowl that can only contain apples.

Yes, .NET can be kind of annoying that way as it can't cast all of your generic parameters in one shot. Instead, perhaps try a generic approach like this to alleviate the problem.
public void LoopThrough<T>(IEnumerable<T> entityList) where T : EntityBase
{
foreach(T entity in entityList)
{
DoSomething(entity as EntityBase);
}
}

The types are compatible but sort of uncompatible, the major reason here is that you are using the base type in parameter as IEnumerable and not the actual type although Entity's base is entitybase because the rules for type parameters and constraints have several implications for generic class behavior, especially regarding inheritance and member accessibility
Generic classes are invariant. In other words, if an input parameter specifies a List<BaseClass>, you will get a compile-time error if you try to provide a List<DerivedClass>.
And thats why you get that error where as in your last e.g. the T is same.
However it would have worked absolutely fine had you used interfaces because all interfaces are compatible
public class Entity : IEntityBase
{ /* Implentation here, not important */ }
public void LoopThrough(IEnumerable<IEntityBase> entityList)
{ foreach(IEntityBase entity in entityList)
{ DoSomething(entity); } }
and than your method will work fine
CustomCollectionType<Entity> entityList; LoopThrough(entityList);
because entitylist has a type of IEntityBase
The other thing you can try is typeof(to get type) or using a cast and it should work

I might be missing something, but if your intent of CustomCollectionType is supposed to be of an Entity base yet allowed to use the IEnumerable, shouldn't you have IT as a base of Entity base first? such as...
public class CustomCollectionType<T> : EntityBase, IEnumerable<T>
{
/* Implementation here, not really important */
}
Then your LoopThrough SHOULD work as the custom collection type is derived FROM the EntityBase and have whatever expected methods, properties, etc available... or worst case, you would type-cast it when calling the function such as
Bowl<Apple> apples = whatever;
LoopThrough((EntityBase)apples);

Related

How to cast A<T> to A<object>? [duplicate]

I know this is old, yet I am still not very good with understanding those problems. Can anyone tell me why the following does not work (throws a runtime exception about casting)?
public abstract class EntityBase { }
public class MyEntity : EntityBase { }
public abstract class RepositoryBase<T> where T : EntityBase { }
public class MyEntityRepository : RepositoryBase<MyEntity> { }
And now the casting line:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
So, can anyone explain how is this invalid? And, I you are not in the mood to explain - is there a line of code I can use to actually do this cast?
RepositoryBase<EntityBase> is not a base class of MyEntityRepository. You're looking for generic variance which exists in C# to a limited extent, but wouldn't apply here.
Suppose your RepositoryBase<T> class had a method like this:
void Add(T entity) { ... }
Now consider:
MyEntityRepository myEntityRepo = GetMyEntityRepo(); // whatever
RepositoryBase<EntityBase> baseRepo = (RepositoryBase<EntityBase>)myEntityRepo;
baseRepo.Add(new OtherEntity(...));
Now you've added a different kind of entity to a MyEntityRepository... and that can't be right.
Basically, generic variance is only safe in certain situations. In particular generic covariance (which is what you're describing here) is only safe when you only ever get values "out" of the API; generic contravariance (which works the other way round) is only safe when you only ever put values "into" the API (e.g. a general comparison which can compare any two shapes by area can be considered as a comparison of squares).
In C# 4 this is available for generic interfaces and generic delegates, not classes - and only with reference types. See MSDN for further information, read <plug>read C# in Depth, 2nd edition, chapter 13</plug> or Eric Lippert's blog series on the topic. Also, I gave a one hour talk about this at NDC in July 2010 - the video is available here.
Whenever someone asks this question, I try to take their example and translate it to something using more well-known classes that is obviously illegal (this is what Jon Skeet has done in his answer; but I'm taking it a step further by performing this translation).
Let's replace MyEntityRepository with MyStringList, like this:
class MyStringList : List<string> { }
Now, you seem to want MyEntityRepository to be castable to RepositoryBase<EntityBase>, the reasoning being that this ought to be possible since MyEntity derives from EntityBase.
But string derives from object, doesn't it? So by this logic we should be able to cast a MyStringList to a List<object>.
Let's see what can happen if we allow that...
var strings = new MyStringList();
strings.Add("Hello");
strings.Add("Goodbye");
var objects = (List<object>)strings;
objects.Add(new Random());
foreach (string s in strings)
{
Console.WriteLine("Length of string: {0}", s.Length);
}
Uh-oh. Suddenly we're enumerating over a List<string> and we come upon a Random object. That's not good.
Hopefully this makes the issue a bit easier to understand.
This requires covariance or contravariance, whose support is limited in .Net, and cannot be used on abstract classes. You can use variance on interfaces though, so a possible solution to your problem is to create an IRepository which you use in place of the abstract class.
public interface IRepository<out T> where T : EntityBase { //or "in" depending on the items.
}
public abstract class RepositoryBase<T> : IRepository<T> where T : EntityBase {
}
public class MyEntityRepository : RepositoryBase<MyEntity> {
}
...
IRepository<EntityBase> baseRepo = (IRepository<EntityBase>)myEntityRepo;

How does c# handle nested (generic) types?

I'm trying to understand how C# views types in the face of nesting.
More specifically I'm trying to understand why some types are not considered assignment compatible or even castable, when there "kind of" only exist one definition of the nested class. Does the compiler / CLR actually generate different types for these, or what rules are at play exactly...
Example code:
public class Foo<T>
{
protected class Private2 : Private1<Foo<T>>
{ }
protected class Private1<T2> where T2 : Foo<T>
{
public sealed class Nested
{
public void Test(T2 foo)
{
foo.Method2(this); //Nope!
var nes = (Private2.Nested)this; //Nope!
}
}
}
public void Method1()
{
var nested = new Private2.Nested();
nested.Test(this);
}
private void Method2(Private2.Nested nested)
{
// something code...
}
}
So even though the nested instance is created as a Private2.Nested it can not be casted to that type. And... well... how do the different Nested types relate to each other given that Nested is in fact sealed? (They can't be inheriting from each other right? But on the other hand their implementation should be 100% identical... am I wrong?)
Primary question: What exactly is the compiler doing when it "compiles" this nested class?? How many unique types (excluding valuetype-related) are actually generated, and if it is all the "same" type, is the restriction artificial (as in wouldn't an unsafe cast actually work)? (What I'm saying is that the IL for all these types comes from the same code definition - so at some level the compiler must know. Are instances of these types not bit-for-bit identical apart from their type-names?)
Secondary question: not what I'm really asking here, mostly for brevity / context: is there some simple change that would make the above work? Am I missing something obvious?
The type Foo<T> must never be directly referenced inside Private1<T2> - only use of T2 is allowed. Foo<T> is just my example stand in for nasty generic classes with 10~20 generic types. It's all just a "workaround" for not being able to alias a generic class with its types:
public class Bar<GoodName, OtherName, Readability, NopeMr, DontThinkSo, Suffering, Dispair>
{
//If only this was real...
using BarT = Bar<GoodName, OtherName, Readability, NopeMr, DontThinkSo, Suffering, Dispair>;
public void Method1(BarT bar) { ... } //so good!!
//goodbye readability... see you never...
public void Method2(Bar<GoodName, OtherName, Readability, NopeMr, DontThinkSo, Suffering, Dispair> whatIsThisVariable) { ... }
}
Purpose: To avoid types of fields and method-parameters that are several screens wide and utterly unreadable! >:(
...As a side note I really wished this could be used as a type inside classes and interfaces, as in Private2 : Private1<this>. Well ok, that wouldn't work because it collides with extension syntax on methods, but something similar, perhaps <this>, <super>, <base> used like Method(<this> arg) or Private2 : Private1<<super>> ... kind of weird maybe.
Consider this types:
public class Base {
public static int Value;
public class Nested { }
}
public class Derived:Base { }
What is Derived.Value and Derived.Nested. Actually, when you refer to inherited static members (nested class considered to be static member) thru derived class, you just reference base class members, so this have exactly same meaning as Base.Value and Base.Nested at compile time. There are no separate static field Derived.Value or separate class Derived.Nested.
public static void Test() {
Derived.Value=10;
Console.WriteLine(Base.Value);
Base.Value=20;
Console.WriteLine(Derived.Value);
Base.Nested bn=new Derived.Nested();
Derived.Nested dn=new Base.Nested();
Console.WriteLine(typeof(Base.Nested).FullName);
Console.WriteLine(typeof(Derived.Nested).FullName);
Console.WriteLine(typeof(Base.Nested)==typeof(Derived.Nested));
}
Original answer:
Foo<A>.Private1<B>.Nested and Foo<C>.Private1<D>.Nested considered to be different types if A!=C or B!=D. They can share same implementation internally, but for assignment compatibility they are different. Foo<T>.Private2.Nested is just alias to Foo<T>.Private1<Foo<T>>.Nested. And even if class Bar:Foo<A>{}, classes Foo<A>.Private1<Foo<A>>.Nested and Foo<A>.Private1<Bar>.Nested still considered to be different types. So Foo<T>.Private1<T2>.Nested can not be converted to Foo<T>.Private1<Foo<T>>.Nested as T2 is not necessary Foo<T>.
You're not thinking with portals. Your inner classes are already generalized on T.
public class Foo<T>
{
private class Private2 : Private1
{ }
private class Private1
{
public sealed class Nested
{
public void Test( Foo<T> foo )
{
foo.Method2( this ); //Yup
var nes = (Private2.Nested)this; //Yup
}
}
}
public void Method1()
{
var nested = new Private2.Nested();
nested.Test( this );
}
private void Method2( Private2.Nested nested )
{
// something code...
}
}
Partial answer to the primary question:
It was bugging me that you can make the code compile by changing Method2 to accept an object and cast it at runtime, because the nested instance is of the correct type (it's instantiated inside Method1). That would seem to work - as long as Foo is sealed - but as soon as someone else can subclass Private1 it is no longer guaranteed to work. (And thus not a solution.) However testing this approach reveals:
Private2.Nested is only a construct of syntax rules - using GetType() on the resulting variable says Private1.Nested and there is no Private2.Nested type.
I think the irksome feeling I was getting from this (and why I concidered sealed to be related) was some kind of confusion on my part when it came to distinguishing between subtype and inheritance. Because the outer classes are inheriting (Private1 and Private2) it feels like inheritance, and thus it feels like it should somehow be castable. But if I understand this correctly they are merely of the same subtype:
There need not be and is in fact no inheritance relation one way or the other (as the sealed clearly hints) because "the inheritance hierarchy is distinct from from the subtype hierarchy", and thus a downright conversion would be needed (since casts are bound to the inheritance hierarchy).

Why does resulting code differ, from two ways of declaring a generic class

Apologies if this has been asked before; I find it difficult to come up with appropriate search terms.
Why is:
public class IsUserAnonymousCondition<T> : WhenCondition<T> where T: RuleContext
{
protected override bool Execute( T ruleContext )
{
...
}
}
different from:
public class IsUserAnonymousCondition : WhenCondition<RuleContext>
{
protected override bool Execute( RuleContext ruleContext )
{
...
}
}
Or rather; do these two not compile to be the excact same? And if not, why not?
They both compile and therefore should both override the same base method.
public abstract class WhenCondition<T> : RuleCondition<T> where T : RuleContext
{
protected abstract bool Execute(T ruleContext);
}
In more practical terms, they are not synonymous in your case due to:
class StrinctRuleContext : RuleContext {}
Have a subclass. we can then have
var aStrinctRuleContext = new StrinctRuleContext();
var aRulesContext = new RuleContext();
whence I can have
new IsUserAnonymousCondition<StrinctRuleContext>().Execute(aRulesContext) which is a compile error, ie the generic version allows a STRICTER type restriction on its inputs than the non-generic version. ie I am free to make a IsUserAnonymousCondition than cannot 'cope' with the base RulesContext class.
the generic version also keeps the ability to add further generic restrictions if it is futher subclassed, ie:
IsUserAnonymousConditionThatNeedsSomeExtraMojo : IsUserAnonymousCondition<T> where T : ISomeExtraMojo
which of couse actualising the type to RuleContext does not.
The two classes provide the same interface, but the simple fact that one class is generic and the other one is not lead to some significant differences.
The most obvious way to see how the two are different would be to try instantiating them from a class name using reflection: in the first case you would have to obtain a generic class by name, make a type representing a generic type instance with RuleContext as its type parameter, and only then create an instance of it. In the second case a name alone would be sufficient to obtain the type.
A more subtle issue is that the types of IsUserAnonymousCondition<RuleCtx1> and IsUserAnonymousCondition<RuleCtx2> instantiated with two different types would be different. This implies that you wouldn't be able to put different IsUserAnonymousCondition<T> objects into a non-generic container.
They are not the same - though in your case there is no functional difference because T is only use as an input.
The difference would be if WhenCondition<T> has a method that used T as an output:
public abstract class WhenCondition<T> : RuleCondition<T> where T : RuleContext
{
protected abstract bool Execute(T ruleContext);
protected abstract T GetContext();
}
Now your two classes would be different, since the actual return type of IsUserAnonymousCondition.GetContext() would not be known at design time (the compiler just knows it derives from RuleContext), but the return type of IsUserAnonymousCondition<T>.GetContext() would be known.
In this case:
WhenCondition<T> where T: RuleContext
T is or derives from RuleContext. The actual type is not known at compile time.
Here it is:
WhenCondition<RuleContext>
It doesn't matter a lot now, but think what the difference will be when doing this:
T GetSomething()
vs.
RuleContext GetSomething()
The return type of the first can be different from the second one.

How to retrieve the argument of a generic base class at compile time? (i.e., without using Reflection)

I want to implement a generic method to retrieve header/detail data from a database:
public static T RetrieveHeaderDetail<T>
where T : Header<???>, new()
// Where ??? means "what can I do here?"
{
// ...
}
Here is the definition of the generic representing a document header:
public class Header<TDetail> where TDetail : class, new()
{
public List<TDetail> Details;
}
And here are some instantiations:
public class RequestForQuotation : Header<RequestForQuotationDetail> { ... }
public class Order : Header<OrderDetail> { ... }
public class Invoice : Header<InvoiceDetail> { ... }
// ..
It is not hard to prove that, since .NET does not allow either multiple inheritance or "generic specialization" (which would allow a Header<U> to derive from some other Header<V>), for any specific T, there is at most one U such that T inherits (directly or indirectly) from Header<U>. Moreover, it is trivial to find the type U: iterate over T's base types until you find an instance of Header<U>, and then just take the generic's argument! Still, C# wants me to specify the change my method's definition to the following:
public static T RetrieveHeaderDetail<T,U>
where T : Header<U>, new()
where U : class, new()
{
// ...
}
Is there any way to get around this problem? I know it would be possible using Reflection, but I think it is a good practice to never do at runtime what could be done at compile time.
When I hit problems like this, I really, really miss C++.
I asked this question not too long ago.
Generics with Generic Parameters and Abstract class
I'm not sure I fully understand what you're after, but could you define an interface and use it to specify the constraint?
For example, we have something like this in a couple places:
public class Reader<T> where T : IInt32Id
{
public T GetById(int Id)
{
// get by id
}
}
Then I just use IInt32Id as an interface to derive all of my classes that have an int (as opposed to long) ID field.

C# generic factory method

Perhaps this is a simple newbie C# question, but so be it---it will be a fresh break from my other questions, which are so difficult that no one knows the answer to them. :)
Let's say I have a generic type in C#:
Thing<T>
And let's say I want to make a thing using a static factory method. In Java, this is no problem:
public static <T> Thing<T> createThing()
{
return flag ? new Thing<Integer>(5) : new Thing<String>("hello");
}
How do I do this in C#? Thanks.
If you want to return an instance of a templated class using one of many different template arguments, one way to do it is with an abstract base (or an interface):
abstract class UntypedThing { }
class Thing<T> : UntypedThing
{
public Thing(T t) { }
}
class Foo
{
public static UntypedThing createThing(bool flag)
{
if (flag)
return new Thing<int>(5);
else return new Thing<String>("hello");
}
}
The UntypedThing class would contain as much code as possible that does not rely on the template type. The Thing class would ideally only contain code that relies on the template type. The factory class Foo always returns the former.
You can in theory use reflection to build up the correct generic type, but it will be pretty useless to you as at some point you will need to upcast it to a less specific type.
public class ThingFactory {
public object Create(bool flag) {
Type outputType = null;
if(flag) {
outputType = typeof(string);
} else {
outputType = typeof(int);
}
return Activator.CreateInstance(typeof(Thing<>).MakeGenericType(outputType));
}
}
As you can see, the value of doing this is about zero as you will need to cast the return type to the type you want, meaning that the logic to determine it needs to live outside the Create method.
I would use Reinderien's method and have a non-generic base. This is the most sane and idiomatic approach.
Oh, the trouble I get myself in when I simply try to do something simple.
It turns out that C# 4 allows this sort of covariance---sort of. First, I have to make Thing an interface and specify the "out" generic parameter:
public interface Thing<out T> {...}
But if I do certain things, C# won't let me use covariance. For example, if I try to return T from the interface:
public interface Thing<out T>
{
public T GetT();
Even if I manage to get covariance with Thing, what do I do with it?
Thing<object> thing=createThing();
The compiler tells me that the type cannot be inferred from usage.
Let's say I say screw the whole T thing and make the factory method return Thing of type object:
public static Thing<object> createThing() {...}
Fine, but now where do I put it?
IList<Thing<object>> list=new List<Thing<object>>();
Thing<object> thing=createThing();
list.Add(thing);
Yes, I have to say that this is a list of Thing with T of type Object, because C# has no wildcard type.
If this were Java, I'd simply say:
public class Thing<T> {...}
public static <T> Thing<T> createThing() {...}
List<?> things=new ArrayList<Thing<?>>();
Thing<?> thing=createThing();
things.add(thing);
If I wanted extra safety by saying that T had to be of a special type, I'd say:
public static <T extends MyBaseType> Thing<T> createThing() {...}
List<? extends MyBaseType> things=new ArrayList<Thing<? extends MyBaseType>>();
Thing<? extends MyBaseType> thing=createThing();
things.add(thing);
Then I'd figure out what T is later, when I had more information.
This all seems to come down to incomplete generic covariance in C# coupled with the lack of C# generic wildcards. (I still maintain it isn't an erasure issue.)
So what do I do? The only simple thing to do seems to follow Reinderien's answer and split out a non-generic base class.
(I wonder if in this non-generic base class I could have object getValue() and then use covariance in the subclass to return T getValue()? Ack, I'm tired of this---I'll leave that for another day.)

Categories

Resources