C# 向下强制转换泛型元素类型 公共类ConfigControlBase:UserControl 其中T:ProviderBase { 公共T提供程序{get;set;} 公共void Init(T提供程序) { this.Provider=Provider; } } 公共抽象类ProviderBase { 公共抽象ConfigControlBase GetControl(); } 公共类提供程序XConfigControl:ConfigControlBase { } 公共类ProviderX:ProviderBase { 公共覆盖ConfigControlBase GetControl() { var confControl=新的ProviderXConfigControl()作为ConfigControlBase; 返回控制; } }

C# 向下强制转换泛型元素类型 公共类ConfigControlBase:UserControl 其中T:ProviderBase { 公共T提供程序{get;set;} 公共void Init(T提供程序) { this.Provider=Provider; } } 公共抽象类ProviderBase { 公共抽象ConfigControlBase GetControl(); } 公共类提供程序XConfigControl:ConfigControlBase { } 公共类ProviderX:ProviderBase { 公共覆盖ConfigControlBase GetControl() { var confControl=新的ProviderXConfigControl()作为ConfigControlBase; 返回控制; } },c#,.net,generics,exception-handling,C#,.net,Generics,Exception Handling,返回控制引发异常: 无法将类型ConfigControlBase隐式转换为ConfigControlBase 这是因为ConfigControlBase不是您的 public class ConfigControlBase<T> : UserControl where T : ProviderBase { public T Provider { get; set; } public void Init(T provider) { th

返回控制引发异常:

无法将类型
ConfigControlBase
隐式转换为
ConfigControlBase


这是因为
ConfigControlBase
不是您的

public class ConfigControlBase<T> : UserControl
    where T : ProviderBase
{
    public T Provider { get; set; }

    public void Init(T provider)
    {
        this.Provider = provider;
    }
}


public abstract class ProviderBase
{
    public abstract ConfigControlBase<ProviderBase> GetControl();
}

public class ProviderXConfigControl : ConfigControlBase<ProviderX>
{
}

public class ProviderX : ProviderBase
{
    public override ConfigControlBase<ProviderBase> GetControl()
    {
        var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>;
        return confControl;
    }
}
公共覆盖ConfigControlBase GetControl()
不匹配

 public override ConfigControlBase<ProviderBase> GetControl()
var confControl=new ProviderXConfigControl()作为ConfigControlBase;

具有可分配类型参数的泛型类型本身不可分配。
例如,您不能将
列表
强制转换为
列表
,尽管
字符串
是一个
对象

现在还不清楚为什么不支持这种强制转换,所以让我举个例子:

var confControl = new ProviderXConfigControl() as ConfigControlBase<ProviderX>;
var words=新列表{“事奉上帝”、“爱我”、“修补”};
变量对象=(列表)单词;//C#编译器不允许这样做
对象。添加(新车());//我们刚给莎士比亚的作品加了一辆汽车,宇宙就爆炸了
然而,C#并不鼓励宇宙爆炸,因为C#4.0实现了这个想法的轻版本。你看,在某些情况下,这种铸造实际上是安全的

.NET 4.0在泛型中引入了协方差和逆变的概念,仅适用于接口和委托,您可能需要查看这一点

示例(在.NET 4.0之前不起作用):

void HandleCollection(IEnumerable集合)
{
// ...
}
var words=新列表{“事奉上帝”、“爱我”、“修补”};
//IEnumerable在.NET4.0中定义为IEnumerable
//“out”关键字保证T仅用于返回值
//因此,客户端代码不能爆炸宇宙
var objects=(IEnumerable)单词;
手工采集(对象);

让我们更改类和属性的名称,但保持形状不变:

void HandleCollection (IEnumerable<object> collection)
{
    // ...
}

var words = new List<string> { "Serve God", "love me", "mend" };

// IEnumerable is defined as IEnumerable<out T> in .NET 4.0
// 'out' keyword guarantees that T is only used for return values
// and therefore client code can't explode the universe   

var objects = (IEnumerable<object>) words;
HandleCollection (objects);
公共类笼子,其中T:动物
{
公共T内容{get;set;}
}
公共级水族馆:笼子{}
公共抽象类动物
{
公共抽象框架GetCage();
}
公营鱼类:动物
{
公共覆盖框架GetCage()
{
返回(笼子)(新水族馆());
}
}
现在清楚为什么这不合法了吗?假设这是合法的。然后你可以这样做:

public class Cage<T> where T : Animal
{
    public T Contents { get; set; }
}

public class Aquarium : Cage<Fish> { }

public abstract class Animal
{
    public abstract Cage<Animal> GetCage();
}

public class Fish : Animal
{
    public override Cage<Animal> GetCage()
    {
        return (Cage<Animal>)(new Aquarium());
    }
}
Fish-Fish=新鱼();
Cage-Cage=fish.GetCage();
cage.contents=新老虎();
现在你的水族馆里有一只老虎。没有人希望这样


编译器(或运行时)必须以某种方式防止这种类型的错误;它选择尽快阻止它。最早可以这样做的是进行从水族馆到
笼子转换的型式试验。编译器知道这最终会导致水族馆中的老虎,因此根本不允许转换。如果您强制编译器允许它通过强制转换,那么它将在运行时失败。

这个答案在您的场景中可能没有用处,因为您可能应该寻找另一个解决方案,但是在反射期间,我发现强制转换为非泛型类型的功能非常有用,因此我为它编写了一个解决方案。但是,它仅适用于接口,并且您必须保证只向接口传递正确类型的对象

我基本上在运行时生成一个代理类,它为您执行所有必需的强制转换。它的用法如下所示:

Fish fish = new Fish();
Cage<Animal> cage = fish.GetCage();
cage.contents = new Tiger();
对象验证程序;//一个已知可实现IValidation的对象。
要验证的对象;//可以使用验证器进行验证的对象。
//假设验证器是IValidation并验证字符串。
竞争性验证
=Proxy.CreateGenericInterfaceWrapper(验证器);
验证。IsValid(toValidate);//这管用!不需要知道类型。
//以下内容将抛出InvalidCastException。
//验证有效(10);

可以找到更多信息和源代码。

请注意,协方差对值类型不起作用。无法将int列表转换为IEnumerable,因为必须为装箱分配内存,并且没有为此发出代码。您可以将字符串列表转换为IEnumerable,因为字符串是引用类型。为了保持一致性,我将示例更改为
string
。谢谢你的评论。当我还是个孩子的时候,当Eric Lippert评论你的答案时,我以为会有某种声誉奖励或荣誉徽章。
Fish fish = new Fish();
Cage<Animal> cage = fish.GetCage();
cage.contents = new Tiger();
object validator;  // An object known to implement IValidation<T>.
object toValidate; // The object which can be validated by using the validator.

// Assume validator is IValidation<string> and toValidate a string.

IValidation<object> validation
    = Proxy.CreateGenericInterfaceWrapper<IValidation<object>>( validator );

validation.IsValid( toValidate ); // This works! No need to know about the type.

// The following will throw an InvalidCastException.
//validation.IsValid( 10 );