Чи існує розумний підхід до параметрів типу “за замовчуванням” у C # Generics?


91

У шаблонах C ++ можна вказати, що певний параметр типу є типовим. Тобто, якщо це явно не вказано, він буде використовувати тип T.

Чи можна це зробити або наблизити в C #?

Я шукаю щось на зразок:

public class MyTemplate<T1, T2=string> {}

Так що екземпляр типу, який явно не вказує T2:

MyTemplate<int> t = new MyTemplate<int>();

По суті:

MyTemplate<int, string> t = new MyTemplate<int, string>();

Зрештою, я розглядаю випадок, коли існує шаблон, який досить широко використовується, але я розглядаю можливість розширення за допомогою додаткового параметра типу. Напевно, я міг би підклас, але мені було цікаво, чи є в цьому ключі інші варіанти.

Відповіді:


76

Підклас - найкращий варіант.

Я б підкласував ваш основний загальний клас:

class BaseGeneric<T,U>

з певним класом

class MyGeneric<T> : BaseGeneric<T, string>

Це полегшує збереження вашої логіки в одному місці (базовий клас), а також надає обидва варіанти використання. Залежно від класу, можливо, для цього потрібно дуже мало додаткової роботи.


1
ах ... це має сенс. Чи дозволено ім'я типу бути однаковим, якщо параметри типу забезпечують унікальний підпис?
el2iot2,

2
@ee: так, загальні засоби визначаються overloadableза кількістю параметрів.
Мехрдад Афшарі,

@ee: Так, але я б насторожився це робити. Це “законно” в .NET робити це, але це може призвести до плутанини. Я волів би, щоб ім'я типу, що походить із рядка, було подібним до основного загального класу (тому очевидно, що це / легко знайти), але ім’я, яке робить очевидним, що це рядок.
Рід Копсі,

@Reed: Чи справді це призводить до плутанини? У цьому конкретному випадку я думаю, що однакові назви навіть допомагають. У .NET є приклади, які роблять те саме: наприклад, делегат Func <>.
Мехрдад Афшарі

4
Наприклад, предикат <T> - це просто Func <T, bool>, але перейменований, оскільки він має іншу мету.
Рід Копсі,

19

Одним із рішень є підкласифікація. Натомість я б використав це заводські методи (поєднані з ключовим словом var).

public class MyTemplate<T1,T2>
{
     public MyTemplate(..args..) { ... } // constructor
}

public static class MyTemplate{

     public static MyTemplate<T1,T2> Create<T1,T2>(..args..)
     {
         return new MyTemplate<T1, T2>(... params ...);
     }

     public static MyTemplate<T1, string> Create<T1>(...args...)
     {
         return new MyTemplate<T1, string>(... params ...);
     }
}

var val1 = MyTemplate.Create<int,decimal>();
var val2 = MyTemplate.Create<int>();

У наведеному вище прикладі val2це тип, MyTemplate<int,string> а не тип, отриманий з нього.

Тип class MyStringTemplate<T>:MyTemplate<T,string>- це не той самий тип, що і MyTemplate<T,string>. Це може спричинити певні проблеми у певних сценаріях. Наприклад, ви не можете відтворити екземпляр MyTemplate<T,string>до MyStringTemplate<T>.


3
Це найбільш корисний підхід. Дуже приємне рішення
T-moty

12

Ви також можете створити клас Перевантаження на зразок цього

public class MyTemplate<T1, T2> {
    public T1 Prop1 { get; set; }
    public T2 Prop2 { get; set; }
}

public class MyTemplate<T1> : MyTemplate<T1, string>{}

Будь ласка, прочитайте інші відповіді, перш ніж публікувати пізню відповідь, тому що, можливо, ваше рішення таке саме, як і інші.
Cheng Chen

7
прийнятою відповіддю було створення класу з іншою назвою, моє рішення - перевантаження того самого класу
Nerdroid

2
Ні, обидва створюють новий клас. Ім'я тут не має значення. MyTemplate<T1>це інший клас від MyTemplate<T1, T2>, ні AnotherTemplate<T1>.
Cheng Chen

7
Навіть якщо реальні типи різні, що є чітким і правильним, кодування простіше, якщо ви зберігаєте те саме ім'я. Для всіх не очевидно, що "перевантаження" класу можна використовувати таким чином. @DannyChen має рацію - з технічної точки зору результати однакові, але ця відповідь ближче до досягнення того, про що просив ОП.
Куба

1
На жаль, це рішення все ще поступається справжнім загальним аргументам "за замовчуванням", оскільки MyTemplate<T1, string>не може бути присвоєно MyTemplate<T1>, що може бути
бажаним

9

C # не підтримує таку функцію.

Як ви вже сказали, ви можете підкласувати його (якщо він не запечатаний і дублювати всі оголошення конструктора), але це зовсім інша річ.


2

На жаль, C # не підтримує те, що ви намагаєтесь зробити. Це було б складно реалізувати, враховуючи, що тип за замовчуванням для параметра повинен дотримуватися загальних обмежень і, швидше за все, створити б головний біль, коли CLR намагався забезпечити безпеку типу.


1
Не зовсім. Це могло бути зроблено з атрибутом (наприклад, параметри за замовчуванням у VB.NET), і компілятор повинен замінити його під час компіляції. Основною причиною є цілі дизайну C #.
Мехрдад Афшарі,

Компілятор повинен переконатися, що параметр за замовчуванням відповідає загальним обмеженням. Також параметром за замовчуванням буде саме загальне обмеження, оскільки будь-які припущення, зроблені щодо параметра типу в методі, вимагатимуть, щоб будь-який параметр типу, який не є типовим, успадковувався від нього.
Ендрю Заєць 02

@Andrew, параметр за замовчуванням не повинен бути загальним обмеженням. Якби це поводилося більше як параметри шаблону за замовчуванням у C ++, то, поширюючись на клас автомата, було б чудово зробити: MyTemplate <int, float> x = null Оскільки T2 не має загальних обмежень, тому float добре, незважаючи на тип рядка за замовчуванням . Таким чином, параметри шаблону за замовчуванням є по суті просто "синтаксичним цукром" для написання MyTemplate <int> як скорочення для MyTemplate <int, string>.
Тайлер Лейнг,

@Andrew, я згоден, що компілятор C # повинен перевірити, чи відповідають такі параметри шаблону за замовчуванням існуючим загальним обмеженням. Компілятор C # вже перевіряє щось подібне в оголошеннях класів, наприклад, додає загальне обмеження, наприклад "where U: ISomeInterface" до класу Reed's BaseGeneric <T, U>, і тоді MyGeneric <T> не зможе скомпілювати з помилкою. Перевірка параметра шаблону за замовчуванням буде майже однаковою: компілятор перевіряє при оголошенні класу, і повідомлення про помилку може бути однаковим або дуже подібним.
Тайлер Лейнг,

"Це було б складно реалізувати" Це нонсенс. Було б тривіально реалізувати. Завдання перевірки типу проти обмежень шаблону не стає складнішим, оскільки тип кандидата з'являється в іншому місці у файлі (тобто в шаблоні, а не під час створення шаблону).
Грязь
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.