繁体   English   中英

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

[英]How to use generic parameter type for another generic class?

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

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)
    {
    }
}

如何使它工作?

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 IHamster不是从BaseHamster派生的。

IHamster 可能还有其他一些实现,它们不是BaseHamster派生的。 并且你的代码不应该因为创建IHamster另一个实现而IHamster对吧 所以编译器不允许这样做,因为你的constaint不限制它。

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

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

public interface 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;
        }
    }

问题出在这条线上。

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);
    }
}

它运作正常。 想象一下,你有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的任何通用参数创建TakeDamageHamster ,并仅使用基本接口对其进行初始化。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM