Приведение обобщенных делегатов к другому типу с интерфейсами - PullRequest
3 голосов
/ 11 января 2012

(Использование .NET 4.0) Итак, у меня есть

private Dictionary<int, Action<IMyInterface, IMyInterface>> handler {get; set;}

public void Foo<T, U>(Action<T, U> myAction)
    where T : IMyInterface
    where U : IMyInterface
    {
        // | This line Fails
        // V
        Action<IMyInterface, IMyInterface> anotherAction = myAction;
        handler.Add(someInt, anotherAction);
    }

Я пытаюсь сохранить делегат в общей коллекции, чтобы потом его можно было извлечь, чтобы вызвать. Как правильно его разыграть?

Ответы [ 3 ]

7 голосов
/ 11 января 2012

Общие параметры для делегата Action являются противоречивыми по типу;они не являются ковариантными типами.В результате вы можете передать меньше определенного типа, но не a больше определенного типа.

Таким образом, компилируется:

protected void X()
{
    Action<string> A = Foo;
}

void Foo(object s) { }

Но это не так:

protected void X()
{
    Action<object> A = Foo;
}

void Foo(string s) { }

Начиная с T and U : IMyInterface, ваш код аналогичен первому примеру.

Intellisense объясняет это довольно четко: (вот увеличенная версия )

enter image description here

1 голос
/ 11 января 2012

Уэлп ... похоже, я и мой друг нашли что-то вроде работы.

public void Foo<T, U>(Action<T, U> myAction)
    where T : IMyInterface
    where U : IMyInterface
    {
        Action<IMyInterface, IMyInterface> anotherAction = (x, y) => eventHandler.Invoke((TSender)x, (TObject),y);
        handler.Add(someInt, anotherAction);
    }

С помощью простой лямбда-обертки мы достигли того, что нам нужно.

0 голосов
/ 11 января 2012

Я не думаю, что есть какой-то безопасный способ сделать то, что вы пытаетесь достичь.Используя пример в обновленном вопросе:

private Dictionary<int, Action<IMyInterface, IMyInterface>> handler {get; set;}

public void Foo<T, U>(Action<T, U> myAction)
    where T : IMyInterface
    where U : IMyInterface
    {
        Action<IMyInterface, IMyInterface> anotherAction = (x, y) => myAction.Invoke((T)x, (U)y);
        handler.Add(someInt, anotherAction);
    }

Предполагается, что IMyInterface и MyImplementation определены следующим образом:

interface IMyInterface
{
    void bar();
}

class MyImplementation : IMyInterface
{
    void IMyInterface.bar()
    {
        //Snip: Do the things
    }

    void nope()
    {
        //Snip: Do other things
    }
}

class MySimplerImplementation : IMyInterface
{
    void IMyInterface.bar()
    {
        //Snip: Do things
    }
}

Мы можем оказаться в следующей ситуации:

void test()
{
    //Create an action with a method that only MyImplementation implements
    Action<MyImplementation, MyImplementation> forMyImplementationOnly =
        (x, y) => x.nope();

    //Use Foo (defined in the example code above) to 'cast' this
    //action and add it to the handler dictionary
    Foo<MyImplementation, Myimplementation>(forMyImplementationOnly);

    //Retrieve the action from the handler dictionary
    Action<IMyInterface, IMyInterface> castedAction = handler[someInt];

    //Try running the action using MySimplerImplementation
    castedAction(new MySimplerImplementation(), new MySimplerImplementation());

    //This code will fail because MySimplerImplementation
    //can not be cast to MyImplementation. It does not even 
    //define the nope() method that your initial Action required
}

Именно по этой причине универсальный Action является контравариантным (вы можете использовать менее конкретные типы, но не более конкретные типы).

...