#.net #delegates #casting
#.net #делегаты #Кастинг
Вопрос:
Я использую сторонний библиотечный класс, который имеет следующий (сокращенный) метод:
public void DoSomethingAsync(Action<ResultInfo> callback)
Вместо того, чтобы ссылаться на эту стороннюю библиотеку в моем проекте кода приложения, я создал класс-оболочку, который абстрагирует сторонние зависимости. Итак, что я пытаюсь сделать, это создать метод-оболочку, который выглядит следующим образом:
public void MyDoSomethingAsync(Action<MyResultInfo> callback)
{
this.wrappedClass.DoSomethingAsync(callback);
}
Проблема в том, что мне нужно преобразовать callback
параметр из Action<MyResultInfo>
в Action<ResultInfo>
. Возможно ли это с помощью пользовательского оператора неявного приведения или есть альтернативный подход, который кто-нибудь может порекомендовать?
Ответ №1:
Нет, вы не смогли бы сделать это напрямую, даже если бы это было MyResultInfo
производным от ResultInfo
; контравариантность делегирования здесь не применялась бы, поскольку Action<MyResultInfo>
не смог бы обрабатывать ни один экземпляр, производный от ResultInfo
(так что это не позволяет использовать Action<MyResultInfo>
вместо Action<ResultInfo>
)
Однако это не означает, что вы не можете использовать адаптер, например:
public void MyDoSomethingAsync(Action<MyResultInfo> callback)
{
// Create the action which will transform ResultInfo
// into MyResultInfo and then pass to the wrapped
// class.
// Create the action first.
Action<ResultInfo> a = ri => {
// Translate ri into MyResultInfo.
MyResultInfo mri = (translate here);
// Call the callback with the translated result.
callback(mri);
};
// Pass the action to the wrapped class.
this.wrappedClass.DoSomethingAsync(a);
}
Комментарии:
1. Я склонен не соглашаться с первым утверждением, потому что контравариантность с делегатами работает прямо противоположно,
Action<Base>
может быть приведена кAction<Derived>
2. Во-вторых, вы связываете точки с контравариантностью неродового делегата, что не работает для
Action<T>
, контравариантность общего делегата была введена только в .net 4.0.3. теперь я полностью согласен с вашим ответом. Но я все еще действительно считаю, что это недостаток дизайна в самом вопросе. Был задан вопрос о том, как сделать неправильную вещь.
Ответ №2:
Проблема в том, что то, что вы хотите создать, может привести к несогласованности. Я предполагаю, что это MyResultInfo
производное от ResultInfo
, потому что в противном случае это не имеет никакого смысла.
Теперь вернемся к несогласованности. Представьте, что у вас есть делегат Action<ResultInfo> callback
. Вызывающий код мог бы вызвать это следующим образом: callback(new MyResultInfo());
и вызвать ваш Action<MyResultInfo>
, и он будет работать нормально. Но представьте, что это вызывается следующим образом: callback(new ResultInfo());
, Action<ResultInfo>
разрешает это, но это приведет к исключению, потому что ResultInfo
не может быть приведено к MyResultInfo
.