This question already has answers here:
Convert List<DerivedClass> to List<BaseClass>
(13 answers)
Closed 5 years ago.
Let's consider that there is an abstract base class and one, or more child classes:
public abstract class BaseInnerClass
{
public int Id { get; set; }
}
public class ConcreteInnerClass : BaseInnerClass
{
public string Name { get; set; }
}
Then, let's assume there is a generic abstract class that has a property of above abstract class type:
public abstract class GeneriAbstractTestClass<T> where T : BaseInnerClass
{
public T InnerClass { get; set; }
}
Then let's make a class that inherits from the class above:
public class ConcreteTestClass : GeneriAbstractTestClass<ConcreteInnerClass>
{
public string ConcreteString { get; set; }
}
So now everything is prepared to ask a question ;) Why it is not possible to do it:
//cannot convert initializer type
GeneriAbstractTestClass<BaseInnerClass> genericClass = new ConcreteTestClass();
while this is allowed:
//ok
BaseInnerClass baseInner = new ConcreteInnerClass();
What's the difference between this two assignments?
This has nothing to do with abstract classes. A simpler example would be
List<BaseInnerClass> base = new List<ConcreteInnerClass>
The fact that type A is derived from type B does not imply that type C<A> is derived from type C<B>. Your example is a little bit more complicated, but it can be explained using the same logic.
Note that you can define another concrete type:
public class EvilConcreteInnerClass : BaseInnerClass
{
}
If what you wanted was possible, then the following would work:
GeneriAbstractTestClass<BaseInnerClass> genericClass = new ConcreteTestClass();
genericClass.InnerClass = new EvilConcreteInnerClass(); // OK, because the compiler sees `T` as `BaseInnerClass`
genericClass variable points to an object whose T generic parameter is ConcreteInnerClass, so assigning EvilConcreteInnerClass to the property would result in a run-time exception.
Actually. You can do this. But you need to specify interface with covariant out T generic, because it is type safe to make those casts.
Example
namespace ConsoleTest
{
class Program
{
static void Main(string[] args)
{
var a = new Generic<Concrete>();
IGeneric<Base> c = new Generic<Base>();
c = a;
}
}
public interface IGeneric<out T> where T: Base
{
T Inner { get; }
}
public class Generic<T> : IGeneric<T>
where T : Base
{
public T Inner { get; set; }
}
public class Concrete : Base
{
}
public class Base
{
}
}
Delegates also not restricted if they specify covariant out generic templates.
It means those casts you want is OK as long as you use readonly generic properties. So, like #Kapol said and provided you example why it is not type safe to allow setters on properties or pass T into function.
Summary
Use ReadOnly interfaces if you want to use those kinds of casts.
Related
This question already has answers here:
Why an inherited interface can't be converted to its base interface in generic context?
(2 answers)
Closed 2 years ago.
I'm trying to write a generic function like the example of the image below.
The idea is to have a method that will receive a generic type that must inherit an abstract class that has a generic configuration which has another abstract class.
public class MainCode
{
public MainCode()
{
Execute<DefaultOptions>();
}
public void Execute<T>() where T : BaseClassOptions<BaseClassConfiguration> { }
}
public class DefaultOptions : BaseClassOptions<DefaultConfiguration> { }
public abstract class BaseClassOptions<T> where T : BaseClassConfiguration
{
public T Config { get; set; }
}
public class DefaultConfiguration : BaseClassConfiguration { }
public abstract class BaseClassConfiguration
{
public string Host { get; set; }
}
But I got the following error:
Could you please help me?
Maybe this will work for you?
public MainCode()
{
Execute<DefaultOptions, DefaultConfiguration>();
}
public void Execute<T, T2>() where T : BaseClassOptions<T2> where T2: BaseClassConfiguration
{ }
The problem is that BaseClassOptions<BaseClassConfiguration> is not the base class for the DefaultOptions, that's why you get this error. It happens because type parameters on generic classes are neither covariant nor contravariant, i.e. there is no inheritance relationship between specializations of the same generic class. If you don't want to use interfaces it may worth to say compiler directly what class to use as a generic parameter with your base type.
Is it possible to create a generic class<T> which the generic type T will be the base class of it?
i.e:
MyClass<Base1> b1 = new MyClass<Base1>();
MyClass<Base2> b2 = new MyClass<Base2>();
b1.Name="test";
b2.ID=1;
Base Classes:
class Base1
{
protected string Name{ get; set;}
}
class Base2
{
protected int ID{ get; set;}
}
Inherited Class:
class MyClass<T>:T //here is the question is it possible dynamic inheritence
{
}
It's possible to use a constraint on T thus forcing T to be of type baseclass, like this:
public class baseclass
{
// base class code
}
// perfectly valid
public class derived1<T> : baseclass where T : baseclass
{
// derived class code
}
It's impossible to compile the following code, since T is a type parameter, and the compiler must infer it from usage. obviously that can't be done like this.
public class derived2<T> : T
{
// derived class code
}
I'm getting a compile error when I try to compile this
The type 'WpfApplication2.CommandInstrumentTrade' cannot be used as type parameter 'T' in the generic type or method 'WpfApplication2.GenericWindowBase'. There is no implicit reference conversion from 'WpfApplication2.CommandInstrumentTrade' to 'WpfApplication2.GenericCommandBase'
public interface IBaseClass
{
int ID { get; set; }
}
public class BaseClass : IBaseClass
{
public int ID { get; set; }
}
public class DerivedClass : BaseClass
{
}
public class Command
{
}
public class GenericCommandBase<T> : Command where T : IBaseClass
{
}
public class DerivedGenericCommand : GenericCommandBase<DerivedClass>
{
}
public class GenericWindowBase<T> where T : GenericCommandBase<IBaseClass>
{
}
public class DerivedGenericWindow : GenericWindowBase<DerivedGenericCommand> // this line fails
{
}
The issue is that Generic<Derived> does not satisfy the condition where T : Generic<Base>. Even if Derived derives from Base, Generic<Derived> does not derive from Generic<Base>
There are many questions like that in StackOverflow.
Try reading those:
C# Generics Inheritance
generic inheritance in C#?
Inheritance doesn't compose with generics. You need to create some kind of converter from one to another. Maybe if you present some less abstract code we could help You
This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
How do I create a list of objects that inherit from the same generic class with varying types?
I'm using several objects where they are inherited from an abstract class. But to use the abstract class must be declara a generic datatype.
I'm having problems because I need to have a list where contains a list of ProblemBase, although each one contains a different TResult datatype.
public abstract class ProblemBase<TResult>
{
TResult[] Array;
}
And I want to get Array property. That's the problem.
This type of thing happens for me quite often. The solution I typically go with is to have a base class for ProblemBase<T> that is type free:
public abstract class ProblemBase
{
public abstract object Result { get; }
}
public abstract class ProblemBase<TResult> : ProblemBase
{
public override object Result
{
get { return Result; }
}
new public TResult Result { get; private set; }
}
Whenever you need a collection of problems, then, you can make a collection of ProblemBase without the generics.
If TResult has its own required inheritance hierarchy, then you can do this instead:
public abstract class ProblemBase
{
public abstract ResultBase Result { get; }
}
public abstract class ProblemBase<TResult> : ProblemBase
where TResult : ResultBase
{
public override ResultBase Result { get { return Result; } }
new public TResult Result { get; private set; }
}
I'm trying to implement a helper method using generics (C# / 3.5)
I've a nice structure of classes, with base classes like so:
public class SomeNiceObject : ObjectBase
{
public string Field1{ get; set; }
}
public class CollectionBase<ObjectBase>()
{
public bool ReadAllFromDatabase();
}
public class SomeNiceObjectCollection : CollectionBase<SomeNiceObject>
{
}
And I wish to retreive collection using a generic method like so:
public class DAL
{
public SomeNiceObjectCollection Read()
{
return ReadFromDB<SomeNiceObjectCollection>();
}
T ReadFromDB<T>() where T : CollectionBase<ObjectBase>, new()
{
T col = new T();
col.ReadAllFromDatabase();
return col;
}
}
This doesn't build, with
Error 66 The type 'SomeNiceObjectCollection' cannot be used as type parameter 'T' in the generic type or method 'ReadFromDB<T>'. There is no implicit reference conversion from 'SomeNiceObjectCollection' to 'CollectionBase<ObjectBase>'.
The SomeNiceObjectCollection object IS a CollectionBase, a CollectionBase to be exact. So how I can get this to work?
C# doesn't support casting between list types (covariance).
Your best bet to support this pattern would be to introduce an interface for the ReadAllFromDatabase method so you are not relying on a generic collection:
public class SomeNiceObject : ObjectBase
{
public string Field1{ get; set; }
}
public interface IFromDatabase
{
bool ReadAllFromDatabase();
}
public class CollectionBase<ObjectBase>() : IFromDatabase
{
public bool ReadAllFromDatabase();
}
public class SomeNiceObjectCollection : CollectionBase<SomeNiceObject>
{
}
public class DAL
{
public SomeNiceObjectCollection Read()
{
return ReadFromDB<SomeNiceObjectCollection>();
}
T ReadFromDB<T>() where T : IFromDatabase, new()
{
T col = new T();
col.ReadAllFromDatabase();
return col;
}
}
In C# 3.0 this is not possible, but with C# and .NET 4.0 with covariance and contravariance, this might be possible.
Think about it, you're taking a collection containing a derived object, and trying to temporarily treat it as a collection of the base object. If this was allowed, you could insert base objects into the list, which would not be of the derived object.
Here, an example:
List<String> l = new List<String>();
List<Object> o = l;
l.Add(10); // 10 will be boxed to an Object, but it is not a String!