русский

C# не работает контравариация в параметрах делегатов

41  
alex445 патриотGestern, 15:09
NEW Gestern, 15:09 
Zuletzt geändert Gestern, 15:26 (alex445)

Есть классы

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, то не сработало бы - и народ в интернете пишут, что так не работает. А наоборот-то почему у меня не пашет?

#1 
alex445 патриотGestern, 15:24
Gestern, 15:24 
in Antwort alex445 Gestern, 15:09, Zuletzt geändert Gestern, 15:27 (alex445)

Что говорит ИИ - и даёт код, аналогичный моему.


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
    }
}
#2 
Отпускник местный жительGestern, 16:13
NEW Gestern, 16:13 
in Antwort alex445 Gestern, 15:09

так вроде ж элементарно

Почему я не могу создать такой делегат через лямбду?

MyDelegate myDel = (Derived obj) => {};

это позволило бы тебе вызывать твой делегат с параметром типа Base


При этом в самом теле лямбды можно приводить параметры и нормально с ними работать:

MyDelegate myDel = (Base obj) =>

{

Derived der = (Derived)obj;

};

тут ты добровольно стреляешь себе в ногу

#3 
Отпускник местный жительGestern, 16:17
NEW Gestern, 16:17 
in Antwort Отпускник Gestern, 16:13

кто-то услышал умное слово, но не понимает значения

Параметры функций являются контравариантными: метод может принимать более общий тип, например Animal вместо Dog.
Но он не может быть более специфичным, то есть принимать только Dog, если делегат ожидает Animal.

#4 
Отпускник местный жительGestern, 16:27
NEW Gestern, 16:27 
in Antwort Отпускник Gestern, 16:17

я хз, что там у тебя за извращенные фантазии, но может тебе поможет такое?

public delegate void AnimalHandler<T>(T animal) where T : Animal;

#5 
alex445 патриотGestern, 17:08
NEW Gestern, 17:08 
in Antwort Отпускник Gestern, 16:13
тут ты добровольно стреляешь себе в ногу

Не, делегат создаётся в том месте, где я точно знаю, как типа параметра передаётся. Там же рядом создаётся объект нужного типа.


это позволило бы тебе вызывать твой делегат с параметром типа Base

А чё ИИ тогда говорит, что зя?

#6 
alex445 патриотGestern, 17:10
NEW Gestern, 17:10 
in Antwort Отпускник Gestern, 16:27
public delegate void AnimalHandler<T>(T animal) where T : Animal;

Я это предложение встречал. Но фреймворк не мой, поэтому работаю с тем, что есть. Может позже это исправим.

#7 
Отпускник местный жительGestern, 19:21
NEW Gestern, 19:21 
in Antwort alex445 Gestern, 17:08
Не, делегат создаётся в том месте, где я точно знаю, как типа параметра передаётся. Там же рядом создаётся объект нужного типа.

Ты точно знаешь, Вася-коллега не знает.

А то вообще можешь во все функции передать объект и внутри кастить.


У тебя в ии режим алекса.

#8 
alex445 патриотGestern, 20:41
NEW Gestern, 20:41 
in Antwort Отпускник Gestern, 19:21, Zuletzt geändert Gestern, 20:43 (alex445)
Ты точно знаешь, Вася-коллега не знает.

Там инициализируется объект и ему тут же назначаются свойства. Одно из свойств - делегат, принимающий этот же объект. Поэтому тип параметра делегата совпадает с типом самого инициализируемого объекта:


var obj = new Obj();

obj.Handler = (BaseObj o) => {...};


Вот только тип параметра делегата сделали зачемто базовым типом для объектов, так что мне в хендлере приходится его всё время приводить.


Я кажется понял, почему. В МСовских UI-фреймворках есть такой паттерн, когда для обработчика события сам объект, для которого событие обрабатывают, передают как object sender - т.е. с наиболее общим типом. А в обработчике ты уже сам его к чему нужно приводишь. Ну и здесь подобное сделали. Но мне надоело приводить, и хочу сразу готовый тип иметь.


А Вася просто должен познакомиться с фреймворком и посмотреть хотя бы пару примеров. В МСовских фреймворках Вася тоже же не с бухты барахты начинает код строчить, как ИИ какой-нибудь?

#9 
alex445 патриотGestern, 20:44
NEW Gestern, 20:44 
in Antwort Отпускник Gestern, 19:21
У тебя в ии режим алекса.

Просто ИИ фуфло. Если не знаешь тему, он тебя рано или поздно обманет (у меня - с первого раза). Если знаешь, тебе его подсказки не нужны.

#10 
Отпускник местный жительGestern, 20:59
NEW Gestern, 20:59 
in Antwort alex445 Gestern, 20:41, Zuletzt geändert Gestern, 21:00 (Отпускник)
Вот только тип параметра делегата сделали зачемто базовым типом для объектов

а как бы сделал ты?

С примером кода.

#11 
alex445 патриотGestern, 21:19
NEW Gestern, 21:19 
in Antwort Отпускник Gestern, 20:59, Zuletzt geändert Gestern, 21:23 (alex445)

чукча не писатель

но передавать все типы через базовый - какой-то костыль

впрочем, читал, что это пошло ещё с первых версий фреймворка, когда не было дженериков

а так, всё это давно надо заменять дженериками


разве что ещё сказали про какойто хэндлер форвардинг - передача сендера арбитражного типа по цепочке хендлеров

#12