Определяемые пользователем ограничения типа времени компиляции в C # - PullRequest
3 голосов
/ 29 декабря 2011

В настоящее время я экспериментирую с дженериками в C # и придумала для себя следующую задачу:

Учитывая обобщенную функцию f<T>, проверьте во время компиляции, что T является типом из заданного набора[T1, T2, ..., Tn].Например, если в f<T> у нас есть

CompileTimeAssert<T>.isContainedIn<TypeList<string, int, bool>>();

, то f<int> должен скомпилироваться, а f<double> не должен скомпилироваться.

Я еще не совсем там.Это то, что у меня есть до сих пор:

interface ContainsType<T> {}

class TypeList<T1>: ContainsType<T1> {} 
class TypeList<T1, T2>: TypeList<T2>, ContainsType<T1>  {}
class TypeList<T1, T2, T3>: TypeList<T2, T3>, ContainsType<T1> {}
class TypeList<T1, T2, T3, T4>: TypeList<T2, T3, T4>, ContainsType<T1> {}
// add longer type lists to taste

class CompileTimeAssert<T>
{
    public static void isContainedIn<TypeList>()
        where TypeList: ContainsType<T> {}
    public static void isContainedIn<TypeList>(TypeList tl)
        where TypeList: ContainsType<T> {}
}

Учитывая приведенный выше код, компилируются следующие (как и ожидалось):

 // uses first overload
CompileTimeAssert<int>.isContainedIn<TypeList<string, int, bool>>();

var myTypeList = new TypeList<string, bool>();
CompileTimeAssert<string>.isContainedIn(myTypeList); // uses second overload

И следующее не компилируется, как и ожидалось:

CompileTimeAssert<short>.isContainedIn<TypeList<string, int, bool>>();

var myTypeList = new TypeList<string, bool>();
CompileTimeAssert<double>.isContainedIn(myTypeList);

Это все очень мило, но также бесполезно.Было бы гораздо полезнее, если бы можно было сделать следующее:

void f<T>()
{
    CompileTimeAssert<T>.isContainedIn<TypeList<string, int, bool>>();
}

, а затем иметь f<int> компиляцию, а f<double> приведет к ошибке компиляции.

Увы, f<T> как указано выше, не компилируется (независимо от вызовов с конкретными типами).Я получаю следующую ошибку (при использовании MonoDevelop в Mac OS X):

Ошибка CS0311: тип TypeList<string,int,bool>' cannot be used as type parameter 'TypeList' in the generic type or method 'CompileTimeAssert<T>.isContainedIn<TypeList>()'. There is no implicit reference conversion from TypeList 'в' ContainsType '

I kindЯ понимаю, почему это не работает, но до сих пор я не смог придумать рабочую альтернативу.У кого-нибудь есть идеи о том, возможно ли вообще то, что я хочу, в C #?

Спасибо.

Ответы [ 3 ]

3 голосов
/ 29 декабря 2011

Цель вашей задачи бесполезна, потому что дженерики .NET являются такой же конструкцией во время выполнения, как и во время компиляции. Даже если ваша программа компилируется без ошибок, вы все равно можете расширить ваш общий вид с помощью отражения, передав «неутвержденный» тип.

Я понимаю, откуда вы пришли (мне тоже очень понравилась эта книга Андрея Александреску), но важно понимать, что в обобщениях на C # - это не шаблоны C ++ . Помимо небольшого синтаксического сходства, они даже не настолько близки: «это не тот же самый стадион, не та же лига, даже не тот же вид спорта». Основная движущая сила, стоящая за необходимостью выполнять проверку во время компиляции, типа «целые числа в порядке, двойные не в порядке») заключалась в том, что вы могли неявно получать доступ к операциям: a+b будет успешным, если типы a и b, предоставленные при расширении шаблона, разрешают операцию +. Это не то же самое в C #: если вы хотите, чтобы операция выполнялась со значением, переданным универсальному, вы должны либо оговорить существование этой операции явно через ограничение типа или предоставьте явный способ выполнения этой операции (делегат, интерфейс и т. д.). В любом случае, сказать компилятору, что ваш шаблон работает для ints nut, а не для double, абсолютно ничего не купит.

0 голосов
/ 29 декабря 2011

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

Единственное, что вы обещали, это (неявно) T : object.

Итак, компилятор проверяет тип

CompileTimeAssert<object>.isContainedIn<TypeList<string, int, bool>>();

и это (правильно) не компилируется.

С другой стороны, вы могли бы написать:

void f<T>() where T : Form
{
    CompileTimeAssert<T>.isContainedIn<TypeList<string, Control>>();
}

и он скомпилируется, поскольку каждый Form является Control.

0 голосов
/ 29 декабря 2011

Я не понимаю, как то, что вы пытаетесь сделать, могло бы быть возможно с помощью дженериков.

Какова цель такого универсального класса (не компилируется, это здесь для примера):

public class GenericClass<T>
    where T : A
    where T : B
    where T : C
{
    public T MyMember { get; set; }

    public GenericClass(T myMember)
    {
        this.MyMember = myMember;
    }
}
  • Если это означает "T должны быть A и B и C", то это невозможно, поскольку в C # не существует мульти-наследования.
  • Если это означает«T должен быть A или B или C», тогда:
    • Если A, B и C не имеют ничего общего, то у GenericClass нет цели: какую логику вы бы реализовали с помощью MyMember, поскольку isn 'Что-нибудь общее для 3 классов?
    • Если у A, B и C есть что-то общее, то все классы реализуют определенный интерфейс или наследуют от одного базового класса, и вы будете использовать этот интерфейс или базукласс для ограничения where.
...