如何将泛型参数类型用于另一个泛型类?

时间:2016-11-22 14:59:13

标签: c# .net generics

对我而言,它看起来像编译器错误或一些奇怪的行为。 编译器无法确定泛型类

中的泛型参数类型

代码

public interface IHamster
{
    int Some { get; set; }
}

public abstract class BaseHamster : IHamster
{
    public int Some { get; set; }
}

public class DerivedHamster : BaseHamster
{
}

class ApplyHitHamster<T> where T : IHamster   // <-- same constraint 
{
    void Zu()
    {
        BaseHamster hamster = null;
        var derived = new DerivedHamster();
        IHamster i = derived;

        var s = new TakeDamageHamster<T>(i); // <<<< Compilation Error on any variables(hamster,derived,i) WHY?????????
        var s2 = new TakeDamageHamster<IHamster>(i); // <<<< But THIS works well
    }
}

class TakeDamageHamster<T> where T : IHamster   // <-- same constraint 
{
    public TakeDamageHamster(T Hamster)
    {
        Console.WriteLine(Hamster.Some);
    }
}

如何才能将<T>where约束相同而不是<IHamster>直接约束?

如果两个类具有相同的where T : IHamster约束,为什么编译器无法确定类型?

修改 另一个简化的例子:

public class BaseHamster
{
    public int Some { get; set; }
}

public class DerivedHamster : BaseHamster
{
}

class ApplyHitHamster<T> where T : BaseHamster, new()   // <-- same constraint 
{
    void Zu()
    {
        BaseHamster hamster = new BaseHamster();
        var derived = new DerivedHamster();

        var s = new TakeDamageHamster<T>();
        s.Method(hamster); // <<<< Compilation Error on any variables(hamster,derived) WHY?????????
    }
}

class TakeDamageHamster<T> where T : BaseHamster, new()  // <-- same constraint 
{
    public void Method(T hamster)
    {
        Console.WriteLine(hamster.Some);
    }
}

另一个例子:

public class BaseHamster
{
    public int Some { get; set; }
}

class ApplyHitHamster<T> where T : BaseHamster, new()   // MSDN: 
{
    void Zu()
    {
        var hamster = new BaseHamster();
        SuperMethod(hamster);  // <<<< WTF? T is ALWAYS BaseHamster!!!
        SuperMethod(hamster as T);    
    }
    void SuperMethod(T x)
    {
    }
}

4 个答案:

答案 0 :(得分:6)

如何使其有效?

1。 为使其正常工作所做的工作就是将其投放到T

BaseHamster hamster = null;
var derived = new DerivedHamster();
T i = derived as T;
var s = new TakeDamageHamster<T>(i);

但是你还需要添加class约束。

class ApplyHitHamster<T> where T : class, IHamster
{
    // Other stuff..
}

2. 或者,您可以更改构造函数以使用界面。那也行。

class TakeDamageHamster<T> where T : IHamster
{
    public TakeDamageHamster(IHamster Hamster)
    {
        Console.WriteLine(Hamster.Some);
    }
}

3。或者您可以使用new T()。请记住,这还要求您添加new()约束。

BaseHamster hamster = null;
var derived = new T();

var s = new TakeDamageHamster<T>(derived); // <<<< Compilation Error on any variables(hamster,derived,i) WHY?????????
var s2 = new TakeDamageHamster<IHamster>(derived); // <<<< But THIS works well

为什么它不起作用?

因为约束不能保证i实际上是从T派生的。我们假设我们创建了AnotherHamster。请注意,它继承自BaseHamster,但不是DerivedHamster

public class DerivedHamster : BaseHamster
{
}

public class AnotherHamster : BaseHamster
{
}

现在我们创建一个ApplyHitHamster

的实例
var fooHamster = new ApplyHitHamster<AnotherHamster>();
fooHamster.Zu(); // Let's pretend that the method is public. :)

这将最终尝试创建TakeDamageHamster<AnotherHamster>的实例。但是等等,您正在尝试向其DerivedHamster发送构造函数。

BaseHamster hamster = null;
var derived = new DerivedHamster();
IHamster i = derived;
// You cannot send DerivedHamster when it expects AnotherHamster.
var s = new TakeDamageHamster<T>(i); // T is now AnotherHamster.

请注意,iDerivedHamster,但TakeDamageHamster<AnotherHamster>需要AnotherHamster。因此它不会编译。

另一个例子。让我们说你是这样初始化你的课程:

var fooHamster = new ApplyHitHamster<BaseHamster>();
fooHamster.Zu();

现在TBaseHamster。这将使代码看起来像这样:

var derived = new DerivedHamster();
IHamster i = derived;
var s = new TakeDamageHamster<BaseHamster>(i); // Cannot pass IHamster when ctor expects BaseHamster.

它不会编译,因为TakeDamageHamster期望BaseHamster(或从它派生的东西)作为它的构造函数的参数。但是你发送了IHamster。即使BaseHamster实现IHamster,它们也不是一回事。 IHamster并非来自BaseHamster

可能IHamster的其他几个实现, 派生自BaseHamster。并且您的代码不应该因为IHamster的另一个实现被创建,正确而中断?所以编译器不允许这样做,因为你的constaint不限制它。

答案 1 :(得分:0)

当传递T时你需要像这样的构造函数,因为你的T只能是IHamster的类型

public TakeDamageHamster(IHamster i)
        {
            // TODO: Complete member initialization
            this.i = i;
        }

公共接口IHamster         {             int some {get;组; }         }

    public abstract class BaseHamster : IHamster
    {
        public int Some { get; set; }
    }

    public class DerivedHamster : BaseHamster
    {
    }

    class ApplyHitHamster<T> where T : IHamster   // <-- same constraint 
    {
        void Zu()
        {
            BaseHamster hamster = null;
            var derived = new DerivedHamster();
            IHamster i = derived;

            var s = new TakeDamageHamster<T>(i); // <<<< Compilation Error on any variables(hamster,derived,i) WHY?????????
            var s2 = new TakeDamageHamster<IHamster>(i); // <<<< But THIS works well
        }
    }

    class TakeDamageHamster<T> where T : IHamster   // <-- same constraint 
    {
        private IHamster i;

        public TakeDamageHamster(T Hamster)
        {
            Console.WriteLine(Hamster.Some);
        }

        public TakeDamageHamster(IHamster i)
        {
            // TODO: Complete member initialization
            this.i = i;
        }
    }

答案 2 :(得分:0)

问题在于这一行。

var s = new TakeDamageHamster<T>(i); 

之所以抛出错误是因为无法保证T的类型为DerivedHamster。也就是说,T保证只是IHamster类型。建议使用以下行。

var s2 = new TakeDamageHamster<DerivedHamster>(derived); 

还要考虑使用辅助方法使代码更容易阅读。

class ApplyHitHamster<T> where T : IHamster  
{
    void Zu()
    {
        var derived = new DerivedHamster();
        var s2 = new TakeDamageHamster<DerivedHamster>(derived); 
        var s3 = CreateTakeDamageHamster(derived);
    }

    TakeDamageHamster<T2> CreateTakeDamageHamster<T2>(T2 hammie)
        where T2 : IHamster 
    {
        return new TakeDamageHamster<T2>(hammie);
    }
}

答案 3 :(得分:0)

一切正常。想象一下,你有ApplyHitHamster<DerivedHamster>。现在,Zu的内容将展开:

IHamster i = derived;

var s = new TakeDamageHamster<DerivedHamster>(i);

您可以清楚地看到TakeDamageHamster的ctor需要DerivedHamster并且您正试图将IHamster传递给它。它无法运作。

如果您希望Zu按原样工作,则需要执行以下操作:

class TakeDamageHamster<T> where T : IHamster   // <-- same constraint 
{
    public TakeDamageHamster(IHamster Hamster)//<-- now this is compatible with constraint alone, no matter what T is.
    {
        Console.WriteLine(Hamster.Some);
    }
}

现在,您可以使用所需的任何通用参数创建TakeDamageHamster,并仅使用基本接口对其进行初始化。