While implementing a design using nested generic collections, I stumbled across those limitations apparently caused by C#'s invariant Generics:
Cannot convert from 'Collection<subtype of T> to 'Collection<T>'
That means, the following will not work, apparently due to the invariance of Generics:
class Outer<TInner, TInnerItem> where TInner : Inner<TInnerItem>
{
public void Add(TInner item)
{
item.Outer = this; // ERROR:
// Cannot implicitly convert from Outer<TInner, TInnerItem>
// to Outer<Inner<TInnerItem>, TInnerItem>
}
}
class Inner<TInnerItem> : ICollection<TInnerItem>
{
Outer<Inner<TInnerItem>, TInnerItem> _outer;
public Outer<Inner<TInnerItem>, TInnerItem> Outer
{
set { _outer = value; }
}
}
(In the actual code, both Inner<> and Outer<> implement ICollection<>.)
I need the Inner<> objects to have a reference to its container collection in order to access some of its data.
How would you implement these nested collections, preferably using a generic approach as outlined above? How would you set the reference to the container collection in the Inner<> class?
Cheers!
Introducing a (possibly abstract) base class which is not dependant on TInner may help you:
abstract class OuterBase<TInnerItem>
{
}
class Outer<TInner, TInnerItem> : OuterBase<TInnerItem> where TInner : Inner<TInnerItem>
{
public void Add(TInner item)
{
item.Outer = this; // Compiles
}
}
class Inner<TInnerItem> : ICollection<TInnerItem>
{
OuterBase<TInnerItem> _outer;
public OuterBase<TInnerItem> Outer
{
set { _outer = value; }
}
}
Or wait for C# 4.0, which introduces co/contra-variant generic interfaces.
The language can't let you convert from Collection<subtype of T> to Collection<T>
Let me explain why.
Imagine you have a Collection<subT> and cast it to Collection<T>. That's ok, since subT inherits from T.
When you retrieve an object from collection_of_T, you're assured that it's ok.
Later, add a T object to collection_of_T. Now you have, in collection_of_subT an object which is not a subT. Whoops.
In order to achieve what you want, you must create a new Collection alltogether.
Collection<T> collection_of_T = new Collection<T>(collection_of_subT);
Which probably is not good for you. Do you really need the TInner generic argument in the Outer class? Would it be possible to replace with Inner<TInnerItem> in the rest of the code instead?
Related
I have a mapping hierarchy of interfaces/classes that use generics constrained by a base class and I am trying to resolve them to their derived classes using the base classes. I am wondering if Autofac can resolve this in a better way than I am currently doing it.
Taking the following structure:
public interface IMapper<T1, T2>
where T1 : FooBase
where T2 : BarBase
{
T1 Map(T2 source);
}
public class FooBarMapper : IMapper<Foo, Bar>
{
public Foo Map(Bar source)
{
return new Foo
{
blahblah = source.xyz
};
}
}
Next, I have a "To" extension method on "Foo" which references the base types, but I want to resolve it to a proper type. I currently resolve like this:
public static TDest To<TDest>(this BarBase o) where TDest : FooBase
{
var genericMapper = typeof(IMapper<,>).MakeGenericType(o.GetType(), typeof(TDest));
object mapper = IocProxy.Container.Resolve(genericMapper);
//... etc
}
This resolves fine... but mapper is just an object. So I'd have to use reflection to hit the "Map" method. Which I'd rather avoid doing. Can autofac do this in a better way so that I end up with an IMapper as opposed to an object? For example, I'd prefer to do something like this, but autofac obviously doesn't resolve it:
var mapper = IocProxy.Container.Resolve<IMapper<FooBase, TDest>>();
The answer to this problem is a bit ugly. Because you can't create the "To" extension method with 1 generic, you have to make the code look ugly so that it can infer both types. Like this:
public static TDest To<TDest,TSource>(this TSource o) where TDest : new()
{
var mapper = IocProxy.Container.Resolve<IMapper<TSource, TDest>>();
return mapper.Map(o);
}
Somewhere deeper down we get this ugly call:
var b = a.To<Bar,Foo>(); //<-- to convert it to "Bar" we need to specify both generics
Yes, ugly! Who really knows what's getting mapped to what here! Unless you explicitly specify the type, but you know how var crazy some people have gone these days.
Anyway, as I don't like this I developed a workaround to improve readability. Firstly, I created a Map class to wrap my object (and my "To" function). This is an extra step, but acts as a nice intermediary for helping autofac resolve the generics correctly:
public class Map<TSource> where TSource : new()
{
public TSource Object { get; set; }
public Map(TSource o)
{
Object = o;
}
public TDest To<TDest>()
{
var mapper = IocProxy.Container.Resolve<IMapper<TSource, TDest>>();
return mapper.Map(Object);
}
}
And then a slightly different generic extension method to get the Mapper back (and get the code into the style I wanted):
public static Map<TDest> Map<TDest>(this TDest o) where TDest : new()
{
return new Map<TDest>(o);
}
This does slightly change how I wanted to call it, but it reads equally as well.
So rather than this:
foo.To<Bar, Foo>();
The code looks like this
foo.Map().To<Bar>()
You could even extend it further and add and interface to the Map class and get Autofac to resolve it, but I guess it depends on how often you are spinning up mappers. And in the end the performance gain is probably negligible.
I'm in the process of trying to create a collection that will only contain one element per type. Each of the types will have to subclass a root type however, so that there is a commonality.
I've done some research and am wondering if I'm on the right track here or if there are some improvements that can be made:
class TypedList<T> : Dictionary<Type, T> {
public override void Add<C>(T instanceOfType) {
base.Add(typeof(C), instanceOfType);
}
}
Specific questions:
Is this the correct/best approach?
What should I do to facilitate retrieving elements?
If the Add() method is correct, is there any way to enforce that C is a subclass of T?
Updated code to reflect Ondrej's suggestions:
class TypedList<Abstract> : IList<Abstract> {
protected Dictionary<Type, Abstract> data;
public void TypedList() {
data = new Dictionary<Type, Abstract>();
}
public void Add<Concrete>(Concrete instanceOfType) where Concrete : Abstract {
data.Add(typeof(Concrete), instanceOfType);
}
}
Many months later and I think I've come up with a really nice solution, which is to use a collection I wasn't aware of initially KeyedCollection:
public class TypedSet<AbstractType> : KeyedCollection<Type, AbstractType> {
protected override Type GetKeyForItem(AbstractType item) {
return item.GetType();
}
}
Very conscice and doesn't require any stitching of different types of collection classes together. Makes for slightly more idiomatic code as well as KeyedCollection is abstract and requires implementation of GetKeyForItem.
This should give you the constraint you're looking for!
class TypedList : Dictionary {
public override void Add<C>(T instanceOfType) where C : T{
base.Add(typeof(C), instanceOfType);
}
}
However, I'm wondering why you're not just using a dictionary straight up?
I.E. the dictionary that you're subclassing should behave the way you waint?
The approach generally seems fine. However, I'd consider hiding the Dictionary<Type, T> as a private field and reimplementing the interface you want. It needs not to be IDictionary<Type, T>, it may as well be just a IList<T>.
Another option is to use List<T> for internal storage, and HashSet<Type> to maintain the set of types of items in the list. Such underlying storage may be better suited in case IList<T> is the wanted interface of TypedList<T>, solving the “missing index” problem with a dictionary.
Also, the Add<C> method declaration seems incorrect. You should declare it as:
public override void Add<C>(C instanceOfType) where C : T {
base.Add(typeof(C), instanceOfType);
}
That makes sure C will be an ancestor of T. In your original declaration C could have been any type, possibly unrelated to T.
I do have a class, which is defined as:
public abstract class Singleton <T> : BaseObject
where T : Singleton <T>
{
}
I want to define an array of those generic singletons somewhere else. Something like
public MonoSingleton[] singletons;
How can I retrieve the proper type of that generic (that seems to be recursive, as you may see)? How can I write this out?
Are you trying to do the 'curiously recursive template pattern', like this?
class CuriouslyRecursiveBase<T>
{
}
class CuriouslyRecursiveDervied<T> : CuriouslyRecursiveBase<T>
{
}
class MyClass : CuriouslyRecursiveBase<MyClass>
{
}
To instantiate the derived from the base, you just use:
class CuriouslyRecursiveBase<T>
{
public static void InstantiateDerived()
{
T instance = (T)Activator.CreateInstance(typeof(T));
}
}
Since T is actually the derived type (MyClass) and curiously is also type (CuriouslyRecursive<MyClass>).
Specifically applied to your problem:
// Create a common interface that all singletons use. This allows
// us to add them all to a list.
interface ISingleton { }
class Singleton<T> : ISingleton
{
// Store our list of ISingletons
static List<ISingleton> instances = new List<ISingleton>();
static T instance;
protected Singleton() { }
public static T GetInstance()
{
// Either return the existing instnace, or create a new one
if (Singleton<T>.instance == null)
{
Singleton<T>.instance = (T)Activator.CreateInstance(typeof(T));
// Use a common interface so they can all be stored together.
// Avoids the previously mentioned co-variance problem.
// Also, compiler isn't built to follow curious recursiveness,
// so use a dynamic statement to force runtime re-evaluation of
// the type hierarchy. Try to avoid dynamic statements in general
// but in this case its useful.
instances.Add((dynamic)Singleton<T>.instance);
}
return Singleton<T>.instance;
}
}
class MyClass : Singleton<MyClass>
{
}
public static void Main()
{
MyClass my = MyClass.GetInstance();
}
More info:
http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
Using design-time code, you'll be able to get the type by using the typeof operator and giving some argument to the generic parameter:
typeof(Singleton<SomeImplementationOfBaseObject>)
Or
typeof(Singleton<>)
But there's an alternative: reflection.
Type singletonType = Type.GetType("NamespaceA.NamespaceN.Singleton`1");
The 1 part is the number of generic parameters. If you've something like Class<T, S> it would be 2 and so on.
Note that using reflection you don't need to give the generic argument. You can get the type with the generic parameter anyway. In order to give the generic argument, you would do this:
Type genericType = singletonType.MakeGenericType(typeof(SomeImplementationOfBaseObject));
Or if you want to get it directly, you would do this:
Type singletonType = Type.GetType("NamespaceA.NamespaceN.Singleton`1[[NamespaceA.NamespaceN.SomeImplementationOfBaseObject]]");
The string inside [[ ]] its the full name for the type passed as generic argument. Note that if the generic type isn't the same assembly as the executing one, you'll need to provide an assembly qualified name (for example, "NamespaceA.MyClass, MyAssembly").
UPDATE
The OP said in some comment:
If I do use: public Singleton<BaseObject>[] singletons;, it warns me
with: 'error CS0309: The type BaseObject' must be convertible to
Singleton' in order to use it as parameter 'T' in the
generic type or method 'Singleton'
This is another problem: you can't do covariance in classes. For doing such thing, you'll need an interface like this:
public interface ISingleton<out TBaseObject> where TBaseObject : .........
And make the Singleton<T> class implement it.
Thus, you can create such array this way:
public ISingleton<BaseObject>[] singletons;
Covariance lets you upcast generic parameters and it's limited to interfaces and delegates.
Learn more here:
http://msdn.microsoft.com/en-us/library/ee207183.aspx
Duplicate
In C#, why can’t a List object be stored in a List variable
Here is my code:
public class Base
{
protected BindingList<SampleBase> m_samples;
public Base() { }
}
public class Derived : Base
{
public Derived()
{
m_samples = new BindingList<SampleDerived>();
}
}
SampleDerived is derived from SampleBase
According to the inheritance logic, I should be able to do this. However, it doesn't compile - the error says that SampleBase can not be be implicitly converted to SampleDerived type. What gives?
I am using c# 2.0
You're trying to use covariance, which is not supported by C# 3.0 and earlier (but will be in C# 4.0). You can still add objects of type SampleDerived into m_samples, but the list's generic type will need to be SampleBase.
Edit: So Pavel is right, C# 4.0 doesn't actually help with this. It would if m_sample were defined as IBindingList<SampleBase> using (fictional) covariant interface IBindingList<out T>.
I can understand why you would think that but generics don't work that way. :(
BindingList<SampleDerived> does not actually derive from BindingList<SampleBase>
A BindingList<SampleDerived> is not a BindingList<SampleBase> -- you can add a SampleBase to the latter, but not to the former.
Generics cannot be casted.
You can cast List<MyClass> to IList<MyClass> or even IList, but this would be illegal:
List<Object> = new List<MyClass>();
Most likely you must simply create the list instance in your base class, and freely use it in derived.
public class Base
{
protected BindingList<SampleBase> m_samples = new BindingList<Derived>();
public Base() { }
}
public class Derived : Base
{
public FwdRunData()
{
m_samples.Add(new Derived>());
}
}
This type of generic variance is not supported in C#2 or 3. It will be supported in C#4. (See comment.) Eric Lippert has a series of blog posts on this subject that goes into enough detail to kill any unwary developer. :)
The people pointing you to Eric Lippert's excellent posts on variance are right, but here's a short example showing what would go wrong. I've just added a new method to the base class and I'm using another derived sample class. What would happen when the BreakDerived method is called on an instance of your original Derived class? You can't add an instance of SampleDerived2 to the BindingList<SampledDerived>.
public class Base {
protected BindingList<SampleBase> m_samples;
public Base() { }
public BreakDerived() { m_samples.Add(new SampleDerived2()); }
}
public class Derived : Base {
public Derived() { m_samples = new BindingList<SampledDerived>(); }
}
I feel my question is pretty dumb, or another way to put it is : I'm too lost in my code to see a workaround for now. Stay too long on a problem, and your vision becomes narrower and narrower ><. Plus I'm not good enough with inheritance, polymorphism and so
Here is the idea : I have multiple list of derived class, and I would like to call generic functions on those lists (accesing and modifying members of the base class). I feel there is something to do with inheritance, but I don't manage to make it work as I want for now .
Here is a very simple example of what I'm intending to do :
class Baseclass
{
public int ID;
public string Name;
}
class DerivedClass1 : Baseclass
{
}
private void FuncOnBase(List<Baseclass> _collection)
{
// ...
foreach (Baseclass obj in _collection)
{
++obj.ID;
}
// ...
}
private void FuncTest()
{
List<DerivedClass1> collection1 = new List<DerivedClass1>();
collection1.Add(new DerivedClass1() { ID = 1 });
collection1.Add(new DerivedClass1() { ID = 2 });
collection1.Add(new DerivedClass1() { ID = 3 });
FuncOnBase(collection1); // ==> forbidden, cannot convert the derived class list to the base class list
}
Gotta love variance. A List<DerivedClass1> is not a List<Baseclass> - otherwise, FuncOnBase could attempt to add a Baseclass to the list, and the compiler wouldn't spot it.
One trick is to use a generic method:
private void FuncOnBase<T>(List<T> _collection) where T : Baseclass
{
// ...
foreach (T obj in _collection)
{
obj.ID++;
}
// ...
}
In terms of the example I presented above - note that we are able to add a T to the list; useful in particular if we add the T : new() constraint, or pass in (for example) a params T[].
Note also that IEnumerable<T> becomes covariant in C# 4.0 / .NET 4.0, so if you passed in just an IEnumerable<Baseclass> (rather than a list) it would work "as is":
private void FuncOnBase(IEnumerable<Baseclass> _collection)
{
///...
}
If you're only doing a foreach, declare FuncOnBase(IEnumerable<Baseclass> collection), which you can call from FuncTest like this:
FuncOnBase(collection1.Cast<Baseclass>());
When you declare a method with a List<T> parameter but only use its IEnumerable<T> features, you're adding API constraints that mean nothing in your code.