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

kal*_*k0v 4 .net c# 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);
    }
}
Run Code Online (Sandbox Code Playgroud)

如何使用<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);
    }
}
Run Code Online (Sandbox Code Playgroud)

另一个例子:

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)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

smo*_*nes 6

如何使它工作?

1.什么你可以做,使之工作丢给T.

BaseHamster hamster = null;
var derived = new DerivedHamster();
T i = derived as T;
var s = new TakeDamageHamster<T>(i);
Run Code Online (Sandbox Code Playgroud)

但是,您还需要添加class约束.

class ApplyHitHamster<T> where T : class, IHamster
{
    // Other stuff..
}
Run Code Online (Sandbox Code Playgroud)

2.或者,您可以更改构造函数以使用该接口.那也行.

class TakeDamageHamster<T> where T : IHamster
{
    public TakeDamageHamster(IHamster Hamster)
    {
        Console.WriteLine(Hamster.Some);
    }
}
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

为什么它不起作用?

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

public class DerivedHamster : BaseHamster
{
}

public class AnotherHamster : BaseHamster
{
}
Run Code Online (Sandbox Code Playgroud)

现在我们创建一个实例ApplyHitHamster.

var fooHamster = new ApplyHitHamster<AnotherHamster>();
fooHamster.Zu(); // Let's pretend that the method is public. :)
Run Code Online (Sandbox Code Playgroud)

这将最终尝试创建一个实例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.
Run Code Online (Sandbox Code Playgroud)

请记住,这i是一个DerivedHamster,但TakeDamageHamster<AnotherHamster>期望一个AnotherHamster.因此它不编译.

另一个例子.假设你像这样初始化你的类:

var fooHamster = new ApplyHitHamster<BaseHamster>();
fooHamster.Zu();
Run Code Online (Sandbox Code Playgroud)

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

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

它不会编译,因为TakeDamageHamster期望一个BaseHamster(或从它派生的东西)作为它的构造函数的参数.但你发送的是IHamster.即使是BaseHamster工具,它们也不是一样的东西IHamster.IHamster不是源于BaseHamster.

可能是其他几个实现IHamster,这并不会从派生BaseHamster.并且你的代码不应该因为IHamster创建另一个实现而破坏,对吧?所以编译器不允许这样做,因为你的constaint不限制它.

  • @ kalashn1k0v - 是的,但是你没有将`T`发送给`TakeDamageHamster`的构造函数.你正在发送一个'DerivedHamster`.并且`DerivedHamster`不一定来自`T`.在你的例子中,它将是你没有其他'BaseHamster`的实现.但是编译器不知道这一点.它不应该因为你添加另一个实现而中断. (2认同)
  • @ kalashn1k0v - `T`不保证是'BaseHamster`因为客户选择`T`并且还可以添加他们自己的'IHamster`实现.如果您只想要多个可能的参数类型`T`那么您根本不需要泛型. (2认同)
  • @ kalashn1k0v - `其中T:BaseHamster`确保`T`是`BaseHamster`的子类型但它确实_not_确保`BaseHamster`是`T`的子类型,这是你需要做的事情.Method(new BaseHamster())`. (2认同)