无法强制转换系统类型的对象.委托[]类型System.Action[T][]

本文关键字:类型 委托 System Action 对象 转换 系统 | 更新日期: 2023-09-27 18:05:40

我试图通过转换Delegate.GetInvocationList的返回值,从某种方法返回Action<T>[]类型的对象,这是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();
    }
}

运行时,抛出以下异常:

Unhandled Exception: System。InvalidCastException:无法转换类型的对象的系统。委托[]'类型'System.Action ' 1[Fish][]'。@ SeniorFishSchef.MentorJuniorSchef()[等等等等]

请帮我理解:

  1. 为什么这样的cast是不可能的?

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

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

注意:除了上述问题的答案之外,还欢迎其他选择,但必须保护委托。我知道的一种方法是简单地从MentorJuniorSchef返回Delegate[],并使dishes_arr具有相同的类型。

无法强制转换系统类型的对象.委托[]类型System.Action[T][]

1。为什么这样的阵容是不可能的?

由于数组类型System.Delegate[]System.Action<Fish>[]不同,因此两者之间也不能隐式转换。请注意,在c#中,有一种类型变化形式允许另一个方向的隐式转换(即从Action<Fish>[]Delegate[]),因为它保证Action<Fish>[]实例中的每个Action<Fish>元素实际上也是Delegate。但是从DelegateAction<Fish>没有这样的保证,所以试图将Delegate[]强制转换为Action<Fish>[]是非法的。

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

当你写显式强制转换时,编译器通常假设你知道你在做什么。通常只有当它确定强制转换失败时才会发出错误(一个值得注意的例外涉及泛型,但在这里把它拖到讨论中会分散注意力)。

请注意,因为从Action<Fish>[]转换为Delegate[]是合法的(见上文),所以实际上可以有一个真正引用Action<Fish>[]实例的Delegate[]引用。因此,在某些情况下,您尝试进行的强制转换可能会成功,因此编译器不会为它发出错误。

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

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

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

是的,这有点令人困惑。

至于替代方法,当然可以只返回一个新数组,其中从原始数组复制了元素。一个简单的方法是:

return cookFishDish.GetInvocationList().Cast<Action<Fish>>().ToArray();

由于您知道单个Delegate对象实际上必须是Action<Fish>的实例,因此使用Enumerable.Cast<T>()方法单独转换每个对象,然后将结果IEnumerable<Action<Fish>>对象转换回数组应该可以实现您想要的效果。