无法将System.Delegate []类型的对象强制转换为System.Action [T] []类型

时间:2016-09-05 18:39:00

标签: c# casting delegates

我正在尝试通过转换Action<T>[]的返回值(类型为Delegate.GetInvocationList)从某个方法返回Delegate[]类型的对象。尝试这样做时会抛出异常。

以下示例演示了我在一些更大的代码中尝试实现的目标:

using System;

class Fish
{ }

class FishDishes
{
    public static void DishA(Fish f) { Console.WriteLine("Dish A"); }
    public static void DishB(Fish f) { Console.WriteLine("Dish B"); }
}

class FishSchef
{
    protected Action<Fish> cookFishDish = null;

    public void ShowOff()
    {
        System.Console.WriteLine("cooking every fish dish I know of!");
        cookFishDish?.Invoke(new Fish());
    }

    public void LearnToCookNewDish(Action<Fish> new_dish)
    {
        cookFishDish += new_dish;
    }
}

class SeniorFishSchef : FishSchef
{ 
    // Mentor a Junior Schef by sending her an array of every dish Senior Schef knows of
    public Action<Fish>[] MentorJuniorSchef()
    {
        return (Action<Fish>[]) cookFishDish.GetInvocationList();
    }
}

class JuniorFishSchef : FishSchef
{ 
     // Get mentored by a Senior Schef by adding sending her an array of every dish Senior Schef knows of
    public void GetMentored(SeniorFishSchef senior)
    {
        Action<Fish>[] dishes_arr = senior.MentorJuniorSchef();
        foreach (Action<Fish> fishdish in dishes_arr)
            cookFishDish += fishdish;
    }
}

class Test
{
    public static void Main()
    {
        SeniorFishSchef senior = new SeniorFishSchef();
        senior.LearnToCookNewDish(FishDishes.DishA);
        senior.LearnToCookNewDish(FishDishes.DishB);
        senior.ShowOff();

        JuniorFishSchef junior = new JuniorFishSchef();
        junior.GetMentored(senior);
        junior.ShowOff();
    }
}

运行时,会抛出以下异常:

  

未处理的异常:System.InvalidCastException:无法转换类型的对象       'System.Delegate []'输入'System.Action`1 [Fish] []'。       在SeniorFishSchef.MentorJuniorSchef()[等等]

请帮助我理解:

  1. 为什么不能进行这样的演员表?

  2. 为什么编译器没有发出错误?

  3. 投射类型的符号是System.Action`1[Fish][] - 为什么它有两个下标([][])?

  4. 注意:除了对上述问题的答案之外,还欢迎其他选择,但必须保护代表。我所知道的只是从MentorJuniorSchef返回Delegate[],并使dishes_arr成为同一类型。

1 个答案:

答案 0 :(得分:1)

  

1.为什么这样的演员不可能?

因为数组类型System.Delegate[]System.Action<Fish>[]不同,也不能将其隐式转换为另一个。请注意,在C#中,有一种类型方差的形式允许隐式转换到另一个方向(即从Action<Fish>[]Delegate[]),因为它保证了每个Action<Fish>元素Action<Fish>[]的实例实际上也是Delegate。但是从DelegateAction<Fish>,没有其他保证,所以尝试将Delegate[]转换为Action<Fish>[]是非法的。

  

2.为什么编译器不会发出错误?

当您编写显式强制转换时,编译器通常会假定您知道自己在做什么。它通常只会发出一个错误,如果它确实知道 ,那么演员表会失败(一个值得注意的例外涉及泛型,但是将这一点拖到讨论中会分散注意力。)

请注意,因为从Action<Fish>[]转换为Delegate[]是合法的(参见上文),实际上您可以使用Delegate[]引用实际引用{{1}的实例1}}。所以在某些情况中,您尝试执行的转换可能会成功,因此编译器无法为其发出错误。

当然,在这种特殊情况下,数组实际上只是Action<Fish>[]而不是Delegate[],因此是运行时错误。但编译器无法确定是否会出现这种情况。

  

3.投射类型的符号是Action<Fish>[] - 为什么它有两个下标(System.Action`1[Fish][])?

[][]周围的方括号不是数组表示法,而是表示泛型Fish的类型参数是什么的方法。未构造的类型名称显示为Action<T>,其中Action`1表示具有一个类型参数的泛型类型。第二组方括号 do 表示数组类型。

是的,这有点令人困惑。

至于替代方案,你当然可以返回一个新数组,你已经复制了原始元素。一个简单的方法是:

`1

由于您知道单个return cookFishDish.GetInvocationList().Cast<Action<Fish>>().ToArray(); 对象实际上必须是Delegate的实例,因此使用Action<Fish>方法逐个转换每个对象,然后将生成的Enumerable.Cast<T>()对象转换回来一个数组应该完成你想要的。