Вы не можете использовать общие ограничения, чтобы выразить интересующие вас ограничения. Обобщения не предназначены для выражения вариации на основе непересекающихся типов - они предназначены для выражения вариации, которая объединена в иерархиитипов (или реализующих определенные интерфейсы).
Однако у вас есть несколько альтернативных вариантов.То, что вы выберете, зависит от точного характера того, что вы пытаетесь сделать.
Используйте методы с разными именами для выражения каждой операции. Я склонен использовать этот подход, когда каждый метод действительно выполняетчто-то другое.Можно утверждать, что возвращение значения другого типа из метода - это, по сути, другая операция, которая заслуживает своего собственного уникального имени.
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 );