Дженерики C# - без нижних границ дизайном?

Я читал интервью с Joshua Bloch в Кодерах на Работе, где он оплакивал введение дженериков в Java 5. Ему не нравится определенная реализация в основном, потому что поддержка различия — подстановочные знаки Java — делают ее излишне сложной.

Насколько я знаю, C# 3 не имеет ничего как явные, ограниченные подстановочные знаки, например, Вы не можете объявить метод PriceBatch, который берет набор Актива или любого подкласса Актива (void PriceBatch(Collection<? extends Asset> assets) в Java?).

Кто-либо знает, почему подстановочные знаки и границы не были добавлены к C#? Эти функции были намеренно не учтены для создания языка более простым, или это что-то, что они просто не обошли для реализации уже?

Править: Святой дым, комментирует от самого Eric Lippert! После чтения проницательных комментариев его и Paul я понимаю, что, по крайней мере, верхние границы поддерживаются и что вышеупомянутый пример может быть переведен в C# как:

void PriceBatch<T>(ICollection<T> assets) where T : Asset

Нижние границы, с другой стороны, по-видимому, не поддерживаются, как Eric говорит в своем втором комментарии, например, нет, вероятно, никакого способа непосредственно перевести это (несколько изобретенный) код Java к C#:

public class Asset {}
public class Derivative extends Asset {}
public class VanillaOption extends Derivative {}

public static <T extends Asset> void copyAssets(Collection<T> src, Collection<? super T> dst) {
    for(T asset : src) dst.add(asset);
}

Collection<VanillaOption> src = new ArrayList<VanillaOption>();
[...]
Collection<Derivative> dst = new ArrayList<Derivative>();
[...]
copyAssets(src, dst);

Я корректен? Если это верно, есть ли конкретная причина, почему C# имеет верхний, но не нижние границы?

17
задан skaffman 9 June 2010 в 21:01
поделиться

3 ответа

Сложный вопрос.

Сначала рассмотрим ваш фундаментальный вопрос: "Почему это запрещено в C#?"

class C<T> where T : Mammal {} // legal
class D<T> where Giraffe : T {} // illegal

То есть, общее ограничение типа может сказать: "T должно быть любым типом ссылки, который может быть присвоен переменной типа Mammal", но не "T должно быть любым типом ссылки, переменной которого может быть присвоен жираф". Почему такая разница?

Я не знаю. Это было задолго до того, как я попал в команду C#. Тривиальный ответ - "потому что CLR его не поддерживает", но команда, которая разработала C# genics, была той же командой , которая разработала CLR genics, так что это на самом деле не так уж и много объяснений.

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

Однако, это предположение. В следующий раз, когда я буду общаться с ребятами, которые работали над дженериками - они живут в Англии, так что, к сожалению, не похоже, чтобы они были внизу по коридору от меня - я спрошу.

Что касается вашего конкретного примера, я думаю, что Пол прав. Вам не нужны нижние ограничения, чтобы это сработало на C#. Можно сказать:

void Copy<T, U>(Collection<T> src, Collection<U> dst) where T : U 
{ 
    foreach(T item in src) dst.Add(item);
}

То есть, поставить ограничение на T, а не на U.

.
23
ответ дан 30 November 2019 в 11:37
поделиться

C# 4 вводит новые возможности, которые позволяют использовать ковариацию и контрвариантность в дженериках.

Есть и другие SO-посты, которые говорят об этом более подробно: Как в C# 4.0 реализована ковариативность и контрастность генерических аргументов?

Новая возможность не включает ее автоматически во всех типах, но появился новый синтаксис, позволяющий разработчикам указывать, являются ли генерические аргументы ковариантными или контрвариантными.

C# версии до C# 4 имели ограниченную функциональность, подобную той, что относится к делегатам и определенным типам массивов. Что касается делегатов, то разрешены делегаты, которые принимают типы базовых параметров. Что касается типов массивов, Я думаю, что это действительно, если только бокс не связан с этим. То есть, массив Заказчика может быть случайным для массива объектов. Однако массив ints не может быть приведен к массиву объектов.

.
8
ответ дан 30 November 2019 в 11:37
поделиться

.net уже имеет эквивалент подстановочных знаков, более логично названные общие ограничения типов, вы можете делать то, что описываете без проблем

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
         List<a> a = new List<a>();
         List<b> b = new List<b>();
         List<c> c = new List<c>();
         test(a);
         test(b);
         test(c);

        }

        static void test<T>(List<T> a) where T : a
        {
            return;
        }
    }
    class a
    {

    }
    class b : a
    {

    }
    class c : b
    {

    }
}

Пример 2

namespace ConsoleApplication3
{
    class Program
    {
        static void Main(string[] args)
        {
            ICollection<VanillaOption> src = new List<VanillaOption>();
        ICollection<Derivative> dst = new List<Derivative>();
         copyAssets(src, dst);
        }

        public static void copyAssets<T,G>(ICollection<T> src, ICollection<G> dst) where T : G {
            foreach(T asset in src) 
                dst.Add(asset);
        }
    }
    public class Asset {}
    public class Derivative : Asset {}
    public class VanillaOption : Derivative {}
}

Этот пример представляет собой приведение кода из вашего примера на java.

Однако я не в состоянии ответить на реальный вопрос!

.
7
ответ дан 30 November 2019 в 11:37
поделиться
Другие вопросы по тегам:

Похожие вопросы: