Как преобразовать действие в определенный делегат с той же подписью?

#c# #generics #delegates

#c# #общие сведения #делегаты

Вопрос:

 class Test
{
    public delegate void FruitDelegate(Fruit f);

    public void Notify<T>(Action<T> del) where T : Fruit
    {
        FruitDelegate f = del; // Cannot implicitly convert type 'Action<T>' to 'FruitDelegate
    }
}
  

Fruit — это пустой класс. Оба этих делегата имеют одинаковую подпись.

Кажется, я не могу заставить что-либо из этого работать. Может быть, это помогло бы, если бы я объяснил, что я пытаюсь сделать (предоставить некоторый контекст).

Я хочу создать класс, который имеет общий статический метод, который предоставляет тип и обратный вызов метода (как в приведенном выше примере).

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

 public void SomeMethod()
{
    Test.Notify<Apple>(AppleHandler);
}

private void AppleHandler(Apple apple)
{

}
  

Вместо этого:

 public void SomeMethod()
{
    Test.Notify<Apple>(AppleHandler);
}

private void AppleHandler(Fruit fruit)
{
    Apple apple = (Apple)fruit;
}
  

Возможно ли такое?

Ответ №1:

это то, что вы хотите?

 static void Main(string[] args)
{

    Program p = new Program();
    p.SomeMethod();
}

public class Fruit
{ }

public class Apple : Fruit { }

public delegate void FruitDelegate<in T>(T f) where T : Fruit;

class Test
{
    public static void Notify<T>(FruitDelegate<T> del)
        where T : Fruit, new()
    {
        T t = new T();
        del.DynamicInvoke(t);
    }
}

private void AppleHandler(Apple apple)
{
    Console.WriteLine(apple.GetType().FullName);
}

public void SomeMethod()
{
    FruitDelegate<Apple> del = new FruitDelegate<Apple>(AppleHandler);
    Test.Notify<Apple>(del);
}
  

Комментарии:

1. Вау… Волшебство! Работает отлично. Мне придется некоторое время смотреть на это. Перемещаем это в начало.

Ответ №2:

Есть веская причина, по которой вы не можете этого сделать. Предположим, что остальная часть вашего метода была:

 class Test
{
    public delegate void FruitDelegate(Fruit f);

    public void Notify<T>(Action<T> del) where T : Fruit
    {
        FruitDelegate f = del; 
        f(new Banana());  //should be legal, but del may be Action<Apple>
    }
}
  

Это определенно не сработало бы, поэтому компилятор здесь прав.

Ответ №3:

Как насчет чего-то подобного?

 public void Notify<T>(Action<T> del) where T : Fruit
{
    FruitDelegate f = fruit => del((T)fruit);
}
  

FruitDelegate Экземпляр при вызове выдаст исключение InvalidCastException, если, скажем, an AppleHandler был вызван с Banana аргументом.