向下转换通用元素类型

Roo*_*ian 7 .net c# generics exception-handling

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

return confControl; 抛出异常:

无法隐式转换ConfigControlBase<ProviderX>ConfigControlBase<ProviderBase>

Eri*_*ert 21

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

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

现在很清楚为什么这不合法?假设它是合法的.然后你可以这样做:

Fish fish = new Fish();
Cage<Animal> cage = fish.GetCage();
cage.contents = new Tiger();
Run Code Online (Sandbox Code Playgroud)

现在你的水族馆里有一只老虎.没有人想要那样.

编译器(或运行时)必须以某种方式防止此类型错误; 它选择尽快防止它.它最早可以做的是从水族馆转换到的类型测试Cage<Animal>.编译器知道这最终会导致水族箱中的老虎,所以它根本不允许转换.如果强制编译器通过强制转换允许它,那么它会在运行时失败.

  • 惊人的比喻.我希望MSDN就是这样. (8认同)

Dan*_*mov 8

具有可分配类型参数的泛型类型本身不可分配.
举例来说,你不能施放List<string>List<object>,虽然string是一个object.

为什么不支持这样的铸造并不是很明显,所以让我举个例子:

var words = new List<string> { "Serve God", "love me", "mend" };
var objects = (List<object>) words; // C# compiler wouldn't allow this
objects.Add (new Car()); // we just added a Car to Shakespeare's work and the universe exploded
Run Code Online (Sandbox Code Playgroud)

C#并不鼓励宇宙爆炸,但是自C#4.0以来,这个想法的一个轻量级版本已经实现.你知道,在某些情况下,这种铸造实际上是安全的.

.NET 4.0 仅为接口和委托带来了泛型中协方差和逆变的概念,您可能需要查看它.

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

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

  • 当我还是个孩子的时候,我认为当Eric Lippert评论你的答案时,会有某种声誉奖励或光荣的徽章. (6认同)
  • "C#不鼓励宇宙爆炸"是我的新座右铭. (4认同)