C# не работает контравариация в параметрах делегатов
Есть классы
class Base {}
class Derived {}
Есть делегат
delegate void MyDelegate(Base obj)
Почему я не могу создать такой делегат через лямбду?
MyDelegate myDel = (Derived obj) => {};
Говорит, типы не совпадают - хочет Base obj (compile error CS1678). При этом в самом теле лямбды можно приводить параметры и нормально с ними работать:
MyDelegate myDel = (Base obj) =>
{
Derived der = (Derived)obj;
};
ИИ пишет, что контравариация в лямбдах есть и первый вариант лямбды должен работать.
.NET 8.
Вот если бы был делегат с параметром Derived, а я бы передавал в лямбде Base, то не сработало бы - и народ в интернете пишут, что так не работает. А наоборот-то почему у меня не пашет?
Что говорит ИИ - и даёт код, аналогичный моему.
C# create delegate through lambda with parameter of type inherited from the original delegate parameter type
using System;
// Base and derived classes
public class Animal
{
public string Name { get; set; }
}
public class Dog : Animal
{
public string Breed { get; set; }
}
// Delegate that takes a base type parameter
public delegate void AnimalHandler(Animal animal);
class Program
{
static void Main()
{
// Create delegate instance using a lambda with a derived type parameter
AnimalHandler handler = (Dog d) =>
{
Console.WriteLine($"Dog name: {d.Name}, Breed: {d.Breed}");
};
// Works with base type argument because delegate expects Animal
handler(new Dog { Name = "Rex", Breed = "German Shepherd" });
// This would cause a runtime cast error if you pass a non-Dog Animal
// handler(new Animal { Name = "Generic Animal" }); // InvalidCastException
}
}
так вроде ж элементарно
Почему я не могу создать такой делегат через лямбду?
MyDelegate myDel = (Derived obj) => {};
это позволило бы тебе вызывать твой делегат с параметром типа Base
При этом в самом теле лямбды можно приводить параметры и нормально с ними работать:
MyDelegate myDel = (Base obj) =>
{
Derived der = (Derived)obj;
};
тут ты добровольно стреляешь себе в ногу
кто-то услышал умное слово, но не понимает значения
Параметры функций являются контравариантными: метод может принимать более общий тип, например Animal вместо Dog.
Но он не может быть более специфичным, то есть принимать только Dog, если делегат ожидает Animal.
тут ты добровольно стреляешь себе в ногу
Не, делегат создаётся в том месте, где я точно знаю, как типа параметра передаётся. Там же рядом создаётся объект нужного типа.
это позволило бы тебе вызывать твой делегат с параметром типа Base
А чё ИИ тогда говорит, что зя?
Не, делегат создаётся в том месте, где я точно знаю, как типа параметра передаётся. Там же рядом создаётся объект нужного типа.
Ты точно знаешь, Вася-коллега не знает.
А то вообще можешь во все функции передать объект и внутри кастить.
У тебя в ии режим алекса.
Ты точно знаешь, Вася-коллега не знает.
Там инициализируется объект и ему тут же назначаются свойства. Одно из свойств - делегат, принимающий этот же объект. Поэтому тип параметра делегата совпадает с типом самого инициализируемого объекта:
var obj = new Obj();
obj.Handler = (BaseObj o) => {...};
Вот только тип параметра делегата сделали зачемто базовым типом для объектов, так что мне в хендлере приходится его всё время приводить.
Я кажется понял, почему. В МСовских UI-фреймворках есть такой паттерн, когда для обработчика события сам объект, для которого событие обрабатывают, передают как object sender - т.е. с наиболее общим типом. А в обработчике ты уже сам его к чему нужно приводишь. Ну и здесь подобное сделали. Но мне надоело приводить, и хочу сразу готовый тип иметь.
А Вася просто должен познакомиться с фреймворком и посмотреть хотя бы пару примеров. В МСовских фреймворках Вася тоже же не с бухты барахты начинает код строчить, как ИИ какой-нибудь?
чукча не писатель
но передавать все типы через базовый - какой-то костыль
впрочем, читал, что это пошло ещё с первых версий фреймворка, когда не было дженериков
а так, всё это давно надо заменять дженериками
разве что ещё сказали про какойто хэндлер форвардинг - передача сендера арбитражного типа по цепочке хендлеров
список