Я пытаюсь понять контравариантность на практике. Когда я прочитал книгу, мне показалось, что это просто, но теперь я застрял. Я понимаю, что есть много тем о контравариантности, и я погуглил многие из них, но ни одна из них не помогла мне понять эту конкретную проблему. Вот что говорится в документации Microsoft https://docs.microsoft.com/en-us/dotnet/standard/generics/covariance-and-contravariance
А вот мой код:
using static System.Console;
namespace CSharpTests
{
class Program
{
delegate void Action<T> (T obj);
static void Main(string[] args)
{
Action<Device> b = DeviceAction;
Action<Mouse> d = b; // Error cannot implicitly convert type CSharpTests.Program.Action<CSharpTests.Device> to CSharpTests.Program.Action<CSharpTests.Mouse>
d(new Mouse());
ReadLine();
}
private static void DeviceAction(Device target) {
WriteLine(target.GetType().Name);
}
}
class Device { }
class Mouse : Device { }
}
В чем принципиальная разница? Мой код даже не компилируется. Как видите, у меня есть делегат, принимающий общий тип, который, насколько я понимаю, допускает контравариантность. Но на практике у меня возникла ошибка времени компиляции. Тоже пытаюсь сделать с параметром "out" и получаю ту же ошибку
using static System.Console;
namespace CSharpTests {
class Program {
delegate void Action<T> (out T obj);
static void Main(string[] args) {
Action<Device> b = DeviceAction;
Action<Mouse> d = b; // Error cannot implicitly convert type CSharpTests.Program.Action<CSharpTests.Device> to CSharpTests.Program.Action<CSharpTests.Mouse>
Mouse m;
d(out m);
ReadLine();
}
private static void DeviceAction(out Device target) {
target = new Device();
WriteLine(target.GetType().Name);
}
}
class Device { }
class Mouse : Device { }
}
Я пробовал это делать и писал об этом. Это дало мне ту же ошибку
Я думаю, вы объявили тип Action со строкой delegate void Action<T> (T obj);
, но он не помечен как контравариантный - для этого вы должны написать delegate void Action<in T> (T obj);
out
в <out T>(...)
имеет другое значение, чем <T>(out T value)
.
Меняем подпись на delegate void Action<in T>
. in
указывает на контравариантность; out
указывает на ковариацию. Обычно вы можете сказать, какой из них использовать, потому что in
предназначен для входы (например, параметров), а out
- для выходы (например, возвращаемых значений).
@ Майк ааааааааааааааааааааааааа, точно! Вы только что сэкономили мне нервы: D Спасибо! К сожалению, я не могу принять комментарий как ответ.
Здесь вы объявляете инвариант T
с out
параметр:
delegate void Action<T> (out T obj);
При переносе out
на Action<out T>
вместо этого вы получите ковариантный T
. Вы хотели сделать это (контравариантный T
с параметром):
delegate void Action<in T> (T obj);
Меняем подпись на delegate void Action<in T>(T arg)
.
Объявление параметра типа как in
указывает на контравариантность; out
указывает на ковариацию.
Обычно вы можете сказать, какой из них использовать, потому что in
предназначен для входы (например, параметров), а out
- для выходы (например, возвращаемых значений).
Итак, вы создаете target = new Device (); это не то же самое, что вы вставили ...
Попробуйте вот так:
[TestClass]
public class Method_Tetsts
{
class Device { }
class Mouse : Device { }
[TestMethod]
public void ActionTest()
{
void DeviceAction<T>( T target)
{
Assert.AreEqual(target.GetType().Name, "Mouse");
}
Action<Device> b = DeviceAction;
Action<Mouse> d = b; // Error cannot implicitly convert type CSharpTests.Program.Action<CSharpTests.Device> to CSharpTests.Program.Action<CSharpTests.Mouse>
d(new Mouse());
}
}
Если вы перейдете по ссылкам, например,
Action
из этого документа, вы увидите, что вы должны украсить параметры типаin
илиout
, чтобы фактически объявить их дисперсию