I have the following code.
class Header<T> where T: IItem { }
class HeaderA : Header<ItemA> { }
class HeaderB : Header<ItemB> { }
interface IItem { }
class ItemA : IItem { }
class ItemB : IItem { }
Header<IItem> h = new HeaderA();
The last line cannot be compiled.
Cannot implicitly convert type 'UserQuery.HeaderA' to 'UserQuery.Header<UserQuery.IItem>'
HeaderA is a subtype of Header and ItemA is a subtype of IItem. Why it doesn't work?
In short, you're trying to use a concept called covariance, which is not supported in .NET generic classes, and not supported by default in interfaces.
If you want to allow the class to do this, you can specify it in C# 3 or later using the out contextual keyword on a generic interface:
interface IHeader<out T> where T : IItem { }
class Header<T>: IHeader<T> where T:IItem { }
class HeaderA : Header<ItemA> { }
class HeaderB : Header<ItemB> { }
interface IItem { }
class ItemA : IItem { }
class ItemB : IItem { }
public void Foo()
{
//now this works; notice the use of the interface instead of the base class.
IHeader<IItem> h = new HeaderA();
}
By using the interface with the keyword, you are basically telling the compiler that no usage of the interface will ever have to know more about the generic type than that it meets the constraints of the interface's generic type declaration (or that it's an object). As such, while you can now assign more derived generics to variables of the interface type, you can only deal with them as the interface type, never as any derived type.
The out keyword is not acceptable for class definitions; you cannot force usages of Header<T> to be covariant.
Related
I have a class "MyClass" which implements generics .
public class MyClass<T>
{
T _base;
MyClass(T child) { _base = child; }
void asdf()
{
_base.MasterFunction();
_base.Variable = true;
}
}
How can I implement the class so that I can access the members of the class im passing in "T"?
MyClass mc = new MyClass<Master>();
mc.asdf(this);
Like this
public class DbException<T> where T : BaseItem
In this case - BaseItem is the generic type that You want Your generic method to operate on
public interface ICar
{
void Start();
}
public class AutoStarter<T> where T : ICar
{
public AutoStarter(T car)
{
car.Start(); //It knows that T implements interface ICar, so you can call Start
}
}
You can use interface constraint, base class.. You can find it here:
MSDN where (generic type constraint) (C# Reference)
MSDN Constraints on Type Parameters (C# Programming Guide)
Let's say I have a set of classes/interfaces:
class ObjectData { }
class UnitData : ObjectData { }
class Component1Data : UnitData { }
class Component2Data : UnitData { }
interface IObject { }
interface IUnit : IObject { }
interface IComponent1 : IUnit { }
interface IComponent2 : IUnit { }
abstract class Object<D, O, I>
where D : ObjectData
where O : Object<D, O, I>, I, new()
where I : IObject
{ }
The point of primary interest here is an Object class which is a base generic class in some hierarchy. Type-param "O" is a way to specify type of an actual class which is derived from the Object. Thus, something like this can be declared and compiled w/o problems:
class Unit : Object<UnitData, Unit, IUnit>, IUnit { }
But what i want to do, is to define another generic "2nd-level" class derived from Object that should also behave as a base class for a couple of similar "3rd-level" entities. And it have to be non-abstract because it is also some sort of an entity. So i need to define something like this:
class Unit<D, I> : Object<D, Unit<D, I>, I>, IUnit
where D : UnitData
where I : IUnit
{ }
class Component1 : Unit<Component1Data, IComponent1>, IComponent1 { }
class Component2 : Unit<Component2Data, IComponent2>, IComponent2 { }
And it produces following compilation error:
error CS0311: The type 'Unit<D, I>' cannot be used as type parameter 'O' in the generic type or method 'Object<D, O, I>'. There is no implicit reference conversion from 'Unit<D, I>' to 'I'.
The question is why? In my vision if Unit<D, I> is implementing IUnit, and param "I" is specified as where I : IUnit, then all should be fine. That's how i see it. What i don't see?
Simplify the problem;
interface IObject { }
interface IUnit : IObject { }
interface IFoo : IUnit { }
abstract class Object<O, I>
where O : Object<O, I>, I, new()
where I : IObject
{}
class Unit : Object<Unit, IUnit>, IUnit
{
}
This is happy and compiles. I've replaced I here with IUnit. Now change to something more derived:
class Unit : Object<Unit, IFoo>, IUnit
and you get the error:
The type 'Unit' cannot be used as type parameter 'O' in the generic
type or method 'Object'. There is no implicit reference
conversion from 'Unit' to 'IFoo'.
So... Unit, which derives from IUnit cannot be converted to IFoo even though both implement IUnit... because Unit doesn't derive from IFoo... which is a condition on Object:
where O : Object<O, I>, I`
which requires you to do something you're not allowed to do :
class Unit<I> : Object<Unit<I>, I>, I
Like others have commented, your generics are too complex.
At least, it seems to me, that there's no need for I type parameter, because your Objects will implement corresponding interface.
So, the code could be simplified like this:
abstract class Object<D, O> : IObject
where D : ObjectData
where O : Object<D, O>
{
}
class Unit<D> : Object<D, Unit<D>>, IUnit
where D : UnitData
{
}
Without full understanding, how O will be used inside of Objects hierarchy, it is hard to say, is it possible to throw away O type parameter.
You've mentioned a static factory method - this is definitely not a reason to bring such complexity. But, of course, you know more about use cases.
I'm implementing an interface that has a member IEnumerable<BaseClass> Member. But I'd like to store some extra information in each BaseClass item, for which I've created a derived class. This is what I'd like to be able to do:
interface IImplementMe
{
IEnumerable<BaseClass> Member { get; }
}
class DerivedClass : BaseClass
{
// Some extra stuff here
}
class Implementation : IImplementMe
{
IEnumerable<DerivedClass> Member { get; }
}
I don't think there's a way to do this (if there is please let me know!). There may be repeated items in the Member list, so I cannot use a dictionary to store the extra stuff. What would it be the standard, elegant way to achieve this?
You can use explicit interface implementations for this.
class Implementation : IImplementMe
{
public IEnumerable<DerivedClass> Member;
IEnumerable<BaseClass> IImplementMe.Member { get { return Member; } }
}
You have two options I think:
Use the derived class as type parameter:
interface IImplementMe<T> where T : BaseClass
{
IEnumerable<T> Member;
}
class DerivedClass : BaseClass
{
// Some extra stuff here
}
class Implementation : IImplementMe<DerivedClass>
{
IEnumerable<DerivedClass> Member;
}
Use IEnumerable:
interface IImplementMe
{
IEnumerable Member;
}
class DerivedClass : BaseClass
{
// Some extra stuff here
}
class Implementation : IImplementMe
{
IEnumerable Member;
}
I have a small class that implements a dictionary that maps from the type of an interface to an implementation of that interface that extends from a base class. Unfortunately the abstract base class does not implement the interfaces, so once in the dictionary, there seems to be no way to associate the two. There is another method in this class that is dependent on storing the objects as BaseClass (in fact, most of my class is dependent on that--the getter into the dictionary is somewhat of a convenience).
private readonly Dictionary<Type, BaseClass> dictionary;
public void Add<T>(BaseClass base)
{
if (!(base is T)) // How to get rid of this check?
{
throw new ArgumentException("base does not implement " + typeof(T).Name);
}
this.dictionary.Add(typeof(T), base);
}
public T Get<T>()
{
BaseClass base;
this.dictionary.TryGetValue(typeof(T), out base);
return (T)(object)base; // How to get rid of (object) cast?
}
Are there any clever constraints I can use to remove the (base is T) check, the cast to object, or both?
Here is the class setup, for reference:
class BaseClass { }
interface IThing { }
class MyClass : BaseClass, IThing { }
dict.Add<IThing>(new MyClass());
IThing myClass = dict.Get<IThing>();
The only way to get the compile-time enforcement you're looking for would be if you have compile-type knowledge of the derived type being added.
For example, if you also specify a type parameter for the class being added then you could constrain that the class implement the interface type parameter:
public void Add<TInterface, TClass>(TClass #base)
where TClass : BaseClass, TInterface {
this.dictionary.Add(typeof(TInterface), #base);
}
So you could do this:
MyClass ok = new MyClass();
dict.Add<IThing, MyClass>(ok);
But not this:
class MyClassNotIThing : BaseClass { }
MyClassNotIThing notOk = new MyClassNotIThing();
dict.Add<IThing, MyClassNotIThing>(notOk);
Aside from that, generic constraints don't offer a means by which to constrain that a known type (i.e. BaseClass) inherit from a generic type parameter.
Here is the solution I ended up using. There are a few tricks that can make the Add() safe without the check (see the link in a comment to cokeman19's answer), but I opted not to do that as I find this code a bit cleaner.
interface IThing { }
abstract class BaseClass
{
internal T AsInterface<T> where T : class
{
return this as T;
}
}
class MyClass : BaseClass, IThing { }
class DictionaryClass
{
private readonly Dictionary<Type, BaseClass> dictionary;
public void Add<T>(BaseClass base)
{
if (base is T)
{
dictionary.Add(typeof(T), base);
}
}
public T Get<T>() where T : class
{
return dictionary[typeof(T)].AsInterface<T>();
}
}
Note that this solution does allow calls like:
myClass.AsInterface<IThingItDoesntImplement>()
but this returns null and I made the function internal to prevent strange uses anyway.
How can I properly inherit a class (of another class and interfaces) that have a generic type with a generic type constraint (where)?
class A { }
class B { }
interface I { }
class C<T> where T : A, B, I { }
In this example A, B and I is treated as base for T. Inhertiance of 2 classes is not possible in C#.
But I want that A is Baseclass of the generic type T and B/I is baseclass/interface of C. How to do this?
As soon as I use the where-constraint for the generic type I cannot derive my class C anymore
public class A
{
}
public class B
{
}
public interface I
{
}
public class C<T> : B, I where T : A
{
}