为什么泛型约束不能被衍生出来?

时间:2010-05-17 07:29:58

标签: c# generics

发现泛型约束不能被衍生到它的衍生类型是非常令人费解的。

假设我有以下代码:

public abstract class BaseClass
{
    public int Version
    { get { return 1; } }

    public string FixString { get; set; }

    public BaseClass()
    {
        FixString = "hello";
    }

    public virtual int GetBaseVersion()
    {
        return Version;
    }
}

public class DeriveClass: BaseClass
{
    public new int Version
    { get { return 2; } }
}

猜猜看,这个方法会返回一个编译错误:

    public void FreeConversion<T>(T baseClass)
    {
       if(baseClass.GetType()==typeof(DeriveClass)
        var derivedMe = (DeriveClass)baseClass;
    }

我必须先将baseClass强制转换为object才能将其投放到DerivedClass,即

    public void FreeConversion<T>(T baseClass)
    {
       if(baseClass.GetType()==typeof(DeriveClass)
        var derivedMe = (DeriveClass)((object)baseClass);
    }

对我来说很难看。为什么会这样?

4 个答案:

答案 0 :(得分:7)

首先,您不应该将基类型变量强制转换为派生类型。它不应该起作用,只是相反。

其次,为什么它通过object工作,是因为你删除了编译时类型检查。编译器可以检查BaseType是否无法转换为DerivedType。但是当变量为object时,编译器会假设您知道自己在做什么。即使它会编译,代码也会在执行期间崩溃。

答案 1 :(得分:3)

答案很简单:编译器无法知道T方法中的FreeConversion可以转换为DeriveClass

答案 2 :(得分:1)

正如你已经说过的,便宜的伎俩是首先施放到物体,然后转移到你想去的类型。丑陋,但它确实有效。

除此之外,可能是你违反了Liskov Substitution原则,没有任何会伤害任何动物的东西,但可以将你的设计推向不可维护的代码。

第三,让你的基类公开派生类型的一个很好的技巧是这样的:

public class Base<T> where T : Base<T> {
  T IAmDerived;
}

public class Derived : Base<Derived> { }

答案 3 :(得分:0)

首先,在您的通用方法中,类型T可以是值类型或引用类型。而它允许你通过'对象'做的原因是,你只是在做装箱拆箱,它适用于系统中的任何类型。

其次。将基类对象转换/转换为派生类将是一个可怕的想法。你违反了OOP的机制。

如果你真的想要返回一个派生自基类的类型的对象,这里有一种方法 - 解决方案与Frank提供的解决方案非常相似。

//This is how you create a function in BaseClass that returns the collection 
//of DerivedTypes when called from an object of type derivedclass, no magic just Generics.

//**The BaseClass**
public class BaseClass<T>
    where T : BaseClass<T>
{
    public HashSet<T> GetHashSet()
    {
        HashSet<T> _hSet = new HashSet<T>();
        //do some work              
        //create a HashSet<T> and return;              
        return _hSet;
    }
}
//**The Derived Class**
public class DerivedClass : BaseClass<DerivedClass>
{
    //you have the method inherited.
}