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.
я хз, что там у тебя за извращенные фантазии, но может тебе поможет такое?
public delegate void AnimalHandler<T>(T animal) where T : Animal;
тут ты добровольно стреляешь себе в ногу
Не, делегат создаётся в том месте, где я точно знаю, как типа параметра передаётся. Там же рядом создаётся объект нужного типа.
это позволило бы тебе вызывать твой делегат с параметром типа Base
А чё ИИ тогда говорит, что зя?
public delegate void AnimalHandler<T>(T animal) where T : Animal;
Я это предложение встречал. Но фреймворк не мой, поэтому работаю с тем, что есть. Может позже это исправим.
Не, делегат создаётся в том месте, где я точно знаю, как типа параметра передаётся. Там же рядом создаётся объект нужного типа.
Ты точно знаешь, Вася-коллега не знает.
А то вообще можешь во все функции передать объект и внутри кастить.
У тебя в ии режим алекса.
Ты точно знаешь, Вася-коллега не знает.
Там инициализируется объект и ему тут же назначаются свойства. Одно из свойств - делегат, принимающий этот же объект. Поэтому тип параметра делегата совпадает с типом самого инициализируемого объекта:
var obj = new Obj();
obj.Handler = (BaseObj o) => {...};
Вот только тип параметра делегата сделали зачемто базовым типом для объектов, так что мне в хендлере приходится его всё время приводить.
Я кажется понял, почему. В МСовских UI-фреймворках есть такой паттерн, когда для обработчика события сам объект, для которого событие обрабатывают, передают как object sender - т.е. с наиболее общим типом. А в обработчике ты уже сам его к чему нужно приводишь. Ну и здесь подобное сделали. Но мне надоело приводить, и хочу сразу готовый тип иметь.
А Вася просто должен познакомиться с фреймворком и посмотреть хотя бы пару примеров. В МСовских фреймворках Вася тоже же не с бухты барахты начинает код строчить, как ИИ какой-нибудь?
У тебя в ии режим алекса.
Просто ИИ фуфло. Если не знаешь тему, он тебя рано или поздно обманет (у меня - с первого раза). Если знаешь, тебе его подсказки не нужны.
Вот только тип параметра делегата сделали зачемто базовым типом для объектов
а как бы сделал ты?
С примером кода.
чукча не писатель
но передавать все типы через базовый - какой-то костыль
впрочем, читал, что это пошло ещё с первых версий фреймворка, когда не было дженериков
а так, всё это давно надо заменять дженериками
разве что ещё сказали про какойто хэндлер форвардинг - передача сендера арбитражного типа по цепочке хендлеров
Liste