Arhn - архитектура программирования

Почему делегирование контравариантности не работает с типами значений?

Этот фрагмент не скомпилирован в LINQPad.

void Main()
{
    (new[]{0,1,2,3}).Where(IsNull).Dump();
}

static bool IsNull(object arg) { return arg == null; }

Сообщение об ошибке компилятора:

Нет перегрузки для «UserQuery.IsNull (объект)» соответствует делегату «System.Func»

Он работает для массива строк, но не работает для int[]. Очевидно, это связано с боксом, но я хочу знать подробности.


  • Это должно быть .Where(x => IsNull(x))? 04.11.2010
  • @ Джоэл Этертон: То же самое (почти). 04.11.2010
  • Попробуйте сделать IsNull универсальным. Отбросьте это, это то, о чем вы спрашиваете :) 04.11.2010

Ответы:


1

Полученный ответ (о том, что нет никаких различий в отношении типов значений) является правильным. Причина, по которой ковариантность и контравариантность не работают, когда один из аргументов переменного типа является типом значения, заключается в следующем. Предположим, что это сработало и показало, как все идет ужасно неправильно:

Func<int> f1 = ()=>123;
Func<object> f2 = f1; // Suppose this were legal.
object ob = f2();

Хорошо, что происходит? f2 идентично по ссылке f1. Следовательно, все, что делает f1, делает f2. Что делает f1? Он помещает 32-битное целое число в стек. Что дает задание? Он берет все, что находится в стеке, и сохраняет его в переменной «ob».

Где была инструкция по боксу? Ее не было! Мы только что сохранили 32-битное целое число в хранилище, которое ожидало не целое число, а скорее 64-битный указатель на место в куче, содержащее упакованное целое число. Таким образом, вы только что сместили стек и испортили содержимое переменной недопустимой ссылкой. Скоро процесс пойдет ко дну.

Так где же должны быть инструкции по боксу? Компилятор должен где-то сгенерировать инструкцию упаковки. Он не может идти после вызова f2, потому что компилятор считает, что f2 возвращает уже упакованный объект. Он не может войти в вызов f1, потому что f1 возвращает int, а не int в штучной упаковке. Он не может идти между вызовом f2 и вызовом f1 , потому что это один и тот же делегат; нет "между".

Единственное, что мы могли бы здесь сделать, это сделать так, чтобы вторая строка действительно означала:

Func<object> f2 = ()=>(object)f1();

и теперь у нас больше нет эталонной идентичности между f1 и f2, так что в чем разница? Весь смысл ковариантных преобразований ссылок заключается в сохранении идентичности ссылок.

Независимо от того, как вы его нарезаете, все идет ужасно неправильно, и нет никакого способа это исправить. Поэтому лучше всего сделать функцию незаконной в первую очередь; в универсальных типах делегатов не допускается отклонение, когда тип значения может быть тем, что изменяется.

ОБНОВЛЕНИЕ: я должен был отметить здесь в своем ответе, что в VB вы можете преобразовать делегата, возвращающего int, в делегата, возвращающего объект. VB просто создает второй делегат, который оборачивает вызов первого делегата и упаковывает результат. VB решает отказаться от ограничения, согласно которому преобразование ссылок сохраняет идентичность объекта.

Это иллюстрирует интересную разницу в принципах проектирования C# и VB. В C# команда разработчиков всегда думает: «Как компилятор может найти то, что может быть ошибкой в ​​программе пользователя, и обратить на это их внимание?» и команда VB думает: «Как мы можем выяснить, что, вероятно, имел в виду пользователь, и просто сделать это от его имени?» Короче говоря, философия C# такова: «Если ты что-то видишь, скажи что-нибудь», а философия VB — «делай то, что я имею в виду, а не то, что я говорю». Обе являются совершенно разумными философиями; интересно наблюдать, как два языка с почти идентичными наборами функций различаются в этих мелких деталях из-за принципов проектирования.

04.11.2010

2

Поскольку Int32 является типом значения, а контравариантность не работает с типами значений.

Вы можете попробовать это:

(new **object**[]{0,1,2,3}).Where(IsNull).Dump();
04.11.2010
  • Dump() может быть методом расширения 04.11.2010

  • 3

    Это не работает для int, потому что нет объектов.

    Пытаться:

    void Fun()
    {
        IEnumerable<object> objects = (new object[] { 0, 1, null, 3 }).Where(IsNull);
    
        foreach (object item in objects)
        {
            Console.WriteLine("item is null");
        }
    }
    
    bool IsNull(object arg) { return arg == null; }
    
    04.11.2010
    Новые материалы

    Коллекции публикаций по глубокому обучению
    Последние пару месяцев я создавал коллекции последних академических публикаций по различным подполям глубокого обучения в моем блоге https://amundtveit.com - эта публикация дает обзор 25..

    Представляем: Pepita
    Фреймворк JavaScript с открытым исходным кодом Я знаю, что недостатка в фреймворках JavaScript нет. Но я просто не мог остановиться. Я хотел написать что-то сам, со своими собственными..

    Советы по коду Laravel #2
    1-) Найти // You can specify the columns you need // in when you use the find method on a model User::find(‘id’, [‘email’,’name’]); // You can increment or decrement // a field in..

    Работа с временными рядами спутниковых изображений, часть 3 (аналитика данных)
    Анализ временных рядов спутниковых изображений для данных наблюдений за большой Землей (arXiv) Автор: Рольф Симоэс , Жильберто Камара , Жильберто Кейрос , Фелипе Соуза , Педро Р. Андраде ,..

    3 способа решить квадратное уравнение (3-й мой любимый) -
    1. Методом факторизации — 2. Используя квадратичную формулу — 3. Заполнив квадрат — Давайте поймем это, решив это простое уравнение: Мы пытаемся сделать LHS,..

    Создание VR-миров с A-Frame
    Виртуальная реальность (и дополненная реальность) стали главными модными терминами в образовательных технологиях. С недорогими VR-гарнитурами, такими как Google Cardboard , и использованием..

    Демистификация рекурсии
    КОДЕКС Демистификация рекурсии Упрощенная концепция ошеломляющей О чем весь этот шум? Рекурсия, кажется, единственная тема, от которой у каждого начинающего студента-информатика..