Динамическое действие‹T›: недопустимые аргументы при выполнении

У меня есть много мест в моем проекте приличного размера, где мне нужно включить тип. Очевидно, что я не могу сделать это в .NET (достаточно простым способом, чтобы удовлетворить меня), поэтому мне нужно выполнить приличное количество кастингов. Этот код является результатом попытки скрыть часть этого в доказательстве концепции.

У меня смоделировано простое наследование:

public class Base { }
public class Derived : Base { public string Name { get; set; } }

и мой класс:

public sealed class TypeSwitch<T> 
{
    private Dictionary<Type, dynamic> _dict;

    public TypeSwitch()
    {
        _dict = new Dictionary<Type, dynamic>();
    }

    public TypeSwitch<T> Add<K>(Action<K> action) where K : T
    {
        _dict.Add(typeof(K), action);
        return this;
    } 

    public void Execute(T item)
    {
        var type = item.GetType();
        var action = _dict[type];

        action(item);
    }
}

и я запускаю его с помощью:

static void Main(string[] args)
{
    var ts = new TypeSwitch<Base>();
    ts.Add<Derived>(d => { Console.WriteLine(d.Name); });

    Base b = new Derived { Name = "Jonesopolis" };
    ts.Execute(b);
}    

Когда я дохожу до action(item), я получаю RuntimeBinderException поговорку

Дополнительная информация: делегат «System.Action‹ConsoleApp.Derived>» имеет недопустимые аргументы.

Это было бы довольно гладко и полезно, если бы я мог заставить его работать. Может кто-нибудь объяснить мне, что мне не хватает? Можно ли заставить это работать?


person Jonesopolis    schedule 10.09.2015    source источник


Ответы (2)


Попробуйте другой уровень лямбд. Помимо работы, я ожидаю, что это будет значительно быстрее, чем использование dynamic, даже несмотря на то, что вызываются два делегата.

public sealed class TypeSwitch<T>
{
    private Dictionary<Type, Action<T>> _dict; // no longer dynamic

    public TypeSwitch()
    {
        _dict = new Dictionary<Type, Action<T>>();  // no longer dynamic
    }

    public TypeSwitch<T> Add<K>(Action<K> action) where K : T
    {
        _dict.Add(typeof (K), o => action((K) o)); // outer lambda casts the value before calling the inner lambda
        return this;
    }

    public void Execute(T item)
    {
        var type = item.GetType();
        var action = _dict[type];
        action(item);
    }
}
person Michael Gunter    schedule 10.09.2015
comment
вау, не думаю, что я когда-либо пробовал это. Огромное спасибо! - person Jonesopolis; 11.09.2015
comment
Любая идея, как бы вы сделали Remove of Action<K> ? - person Wesley; 02.10.2020

Ваш параметр item не равен dynamic. Поскольку он статически типизирован как T, этот тип T (который оказывается Base) будет использоваться для разрешения перегрузки. Action<Derived> нельзя вызвать с аргументом Base.

Чтобы использовать здесь dynamic, вам также нужно сделать item dynamic: измените action(item); на action((dynamic) item);.

person Community    schedule 10.09.2015