简体   繁体   English

泛型约束在C#中似乎不起作用

[英]Generics constraint doesn't appear to work in C#

error CS0029: Cannot implicitly convert type `Manager<T>' to `Manager<Asset>'

I don't understand why I'm getting the above compile-time error with the code below. 我不明白为什么我在下面的代码中遇到上述编译时错误。 Since the T type associated with Manager must derive from Asset, shouldn't it work? 由于与Manager关联的T类型必须从Asset派生,所以它行不通吗?

public class Manager<T> where T : Asset
{
    public Manager()
    {
        var t = new Test();
        t.Manager = this; //compile-time error
    }
}

public class Test
{
    public Manager<Asset> Manager;
}

Since the T type associated with Manager must derive from Asset, shouldn't it work? 由于与Manager关联的T类型必须从Asset派生,所以它行不通吗?

No, because the type parameter in generic classes is not covariant . 不可以,因为通用类中的类型参数不是协变的 That is, if you have DerivedAsset : Asset , then you are not allowed to assign Manager<Asset> manager = new Manager<DerivedAsset>() . 也就是说,如果您具有DerivedAsset : Asset ,则不允许您分配Manager<Asset> manager = new Manager<DerivedAsset>()

You can make a type parameter covariant by using out T , but this is only possible for interfaces. 您可以使用out T使类型参数协变,但这仅适用于接口。 So you would need this: 因此,您将需要以下内容:

public interface IManager<out T> where T : Asset { }

public class Test
{
    public IManager<Asset> Manager;
}

Now you should be able to write your original code, making Manager<T> implement IManager<T> : 现在您应该能够编写原始代码,使Manager<T>实现IManager<T>

public class Manager<T> : IManager<T> 
    where T : Asset
{
    public Manager()
    {
        var t = new Test();
        t.Manager = this;     // ok, because T in IManager<T> is covariant
    }
}

(As @sjkm noted, you could also simply do a cast: t.Manager = (Manager<Asset>)this; . But this would only work if you are sure that the T in Manager<T> is never a derived class -- if Asset were sealed, for example.) (如@sjkm指出,你也可以简单地做一个投: t.Manager = (Manager<Asset>)this;但如果你是确保这只会工作TManager<T>从来都不是一个派生类- -例如,如果Asset被密封。)

Just because a T is an Asset doesn't mean that a Manager<T> is a Manager<Asset> . 仅仅因为TAsset并不意味着Manager<T>Manager<Asset> It would work if Manager<T> was covariant in T , but classes can't be covariant in C# (only interfaces and delegates can). 如果Manager<T>T是协变的,但在C#中类不能是协变的(仅接口和委托可以),这将起作用。

Instead, you can make Test generic as well: 相反,您也可以使Test通用:

public class Test<T> where T : Asset
{
    public Manager<T> Manager;
}

And it should now work as expected. 现在它应该可以按预期工作了。

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

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