Why does the following doesn't compile? (involves generics and inheritance in c#)
This compiles:
class ReplicatedBaseType
{
}
cl开发者_开发百科ass NewType: ReplicatedBaseType
{
}
class Document
{
ReplicatedBaseType BaseObject;
Document()
{
BaseObject = new NewType();
}
}
But this does not:
class DalBase<T> : where T: ReplicatedBaseType
{
}
class DocumentTemplate
{
DalBase<ReplicatedBaseType> BaseCollection;
DocumentTemplate ()
{
BaseCollection= new DalBase<NewType>(); // Error in this line. It seems this is not possible
}
}
What's the reason?
As Andrey says, you want (generic) covariance. However:
- Generic variance is only supported in C# 4
- Generic variance isn't supported on classes
- In your real life case, this may be unsafe.
To go into the final point, suppose DalBase<T>
has this method:
void AddEntity(T entity)
Now you've got something like this which you want to be able to compile - but would obviously be dangerous:
DalBase<Fruit> fruitDal = new DalBase<Banana>();
fruitDal.AddEntity(new Apple());
The second line would have to compile - so for this to fail at compile time, it has to be the first line which would fail.
I gave an hour long talk on generic variance recently which you may find useful if you want to know more - see the NDC 2010 video page and search for "variance". Alternatively you could read Eric Lippert's blog posts on the topic - but be aware that that will probably take longer than an hour ;)
Variance exists in C# 4.0 targetting .NET 4), but is limited to interfaces and usage of in
/out
(oh, and arrays of reference-types). For example, to make a covariant sequence:
class DalBase<T> : IEnumerable<T> where T: ReplicatedBaseType
{
public IEnumerator<T> GetEnumerator() {throw new NotImplementedException();}
IEnumerator IEnumerable.GetEnumerator() { throw new NotImplementedException(); }
}
class DocumentTemplate
{
IEnumerable<ReplicatedBaseType> BaseCollection;
DocumentTemplate()
{
BaseCollection = new DalBase<NewType>(); // Error in this line. It seems this is not possible
}
}
But other than that... no. Stick to either non-generic lists (IList
), or use the expected list type.
It doesn't work because a DalBase<NewType>
is not a DalBase<ReplicatedBaseType>
- generics do not have co/contra-variance. In C# 4 you can get generic variance, but only on interfaces and delegates.
the feature that you want is called "covariance" and was introduced only in C# 4.0 http://blog.t-l-k.com/dot-net/2009/c-sharp-4-covariance-and-contravariance
you code fails because there is no such cast (both explicit and implicit). Current rules of C# (<= 3.0) forbid it.
to check possible casts see §6.1.6 C# Specification
This code compiles in VS2010, net framework 4
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
namespace ConsoleApplication2
{
class ReplicatedBaseType
{
}
class NewType : ReplicatedBaseType
{
}
class Document
{
ReplicatedBaseType BaseObject;
Document()
{
BaseObject = new NewType();
}
}
interface DalBase<out T> where T: ReplicatedBaseType
{
}
class DalBaseExample<T> : DalBase<T> where T: ReplicatedBaseType
{
}
class DocumentTemplate
{
DalBase<ReplicatedBaseType> BaseType;
DocumentTemplate ()
{
BaseType = new DalBaseExample<NewType>(); // no error here
}
}
class Program
{
static void Main(string[] args)
{
}
}
}
精彩评论