Ограничить тип до определенных типов - PullRequest
3 голосов
/ 04 августа 2010

Можно ли ограничить универсальный метод определенными типами?

Я хочу написать что-то вроде этого:

public T GetValue<T>(string _attributeValue) where T : float, string
{
    return default(T); // do some other stuff in reality
}

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

Редактировать: Ack. Я знал, string не тип значения. Я начал с двух числовых типов ранее. К сожалению.

Ответы [ 4 ]

5 голосов
/ 04 августа 2010

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

Однако у вас есть несколько альтернативных вариантов.То, что вы выберете, зависит от точного характера того, что вы пытаетесь сделать.

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

float GetFloat(string attrName) { }
string GetString(string attrName) { }

Укажите «значение по умолчанию», чтобы тип мог бытьВывод. Во многих проектах, где вы запрашиваете значение по имени, полезно указать значение по умолчанию.Это может позволить вам использовать перегрузку, чтобы различать, какой метод вызывать (в зависимости от типа значения по умолчанию).К сожалению, этот подход довольно хрупок и легко ломается при передаче литеральных значений в перегрузки, которые принимают числовые примитивы (int против uint против long).

float GetValue(string attrName, float defaultValue) { ... }
string GetValue(string attrName, string defaultValue) { ... }

Используйте универсальный метод, но бросьтеисключение времени выполнения, если тип не из тех, которые вы поддерживаете. Лично я нахожу этот вид уродливым и нарушающим дух обобщений - обобщение должно объединять функциональность в иерархии или наборе типов, реализующих некоторый интерфейс.Однако в некоторых случаях имеет смысл это сделать (если, скажем так, один конкретный тип не может быть поддержан, скажем).Другая проблема с этим подходом состоит в том, что сигнатура универсального метода не может быть выведена из каких-либо параметров, поэтому вам нужно будет указать желаемый тип при его вызове ... в этот момент он не намного лучше (с точки зрения синтаксиса)чем использование разных имен методов.

T GetValue<T>( string attrName )
{
   if( typeof(T) != typeof(string) ||
       typeof(T) != typeof(float) )
       throw new NotSupportedException();
   return default(T); 
}

// call it by specifying the type expected...
float f = GetValue<float>(attrName);
string s = GetValue<string>(attrName);

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

void GetValue( string attrName, out float value )
void GetValue( string attrName, out string value )

// example of usage:
float f;
GetValue( attrName, out f );
string s;
GetValue( attrName, out s );
2 голосов
/ 04 августа 2010

Нет, это невозможно.

И строка является ссылочным типом, а не типом значения.

Самое близкое, что вы можете получить, это ограничение всех типов значений (за исключением типов Nullable):

public T GetValue<T>(string _attributeValue) where T : struct

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

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

private T GetValue<T>(string _attributeValue) where T : struct
{
    return default(T);
}

public float GetFloatValue(string _attributeValue)
{
    return GetValue<float>(_attributeValue);
}

public int GetIntValue(string _attributeValue)
{
    return GetValue<int>(_attributeValue);
}

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

2 голосов
/ 04 августа 2010

Это невозможно сделать с поддержкой времени компиляции. Вы можете сделать эту проверку в статическом конструкторе и сгенерировать исключение (в случае, если T определено для типа) или (в вашем случае) в самом теле метода, но в этом случае это будет проверка во время выполнения.

1 голос
/ 04 августа 2010

Нет, вы не можете указать диапазон типов. Если вы хотите все приматы, которые вы можете сделать (и я знаю, что строка не включена)

 where T: struct
...