Значение отливки T в общем методе
У меня есть интерфейс для скрипичной карты свойств:
interface IPropertyMap
{
bool Exists(string key);
int GetInt(string key);
string GetString(string key);
//etc..
}
Я хочу создать метод расширения следующим образом:
public static T GetOrDefault<T>(this IPropertyMap map, string key, T defaultValue)
{
if (!map.Exists(key))
return defaultValue;
else
{
if (typeof(T) == typeof(int)) return (T)map.GetInt(key);
//etc..
}
}
Но компилятор не даст мне нажать на T. Я попробовал добавить where T : struct
, но это, похоже, не помогает.
Что мне не хватает?
Ответы
Ответ 1
Я считаю, что это связано с тем, что компилятор не знает, какой тип операции он должен выполнять. IIRC, вы можете заставить его работать, если вы вводите бокс:
if (typeof(T) == typeof(int)) return (T)(object)map.GetInt(key);
но это не идеально с точки зрения производительности.
Я думаю, что это просто ограничение дженериков, к сожалению.
Ответ 2
Что делать GetInt
, GetString
и т.д. внутренне? Могут быть другие варианты, включающие Convert.ChangeType(...)
или TypeDescriptor.GetConverter(...).ConvertFrom(...)
, и один листинг с использованием индексатора объектов:
например, если объекты уже правильно введены:
public T GetOrDefault<T>(this IPropertyMap map, string key, T defaultValue)
{
return map.Exists(key) ? (T)map[key] : defaultValue;
}
или если они хранятся в виде строк и нуждаются в преобразовании, что-то включает в себя:
T typedVal = (T) TypeDescriptor.GetConverter(typeof(T)).ConvertFrom(map[key]);
Ответ 3
Я предполагаю, что это просто опечатка, но bool GetInt(string key)
кажется странным. Он должен быть int GetInt(string key)
или еще лучше int GetInt32(string key)
.
Далее, Джон уже отметил, что для вашего кода требуется бокс, так что вы это делаете.
И, наконец, добавьте метод "catch-all" в ваш интерфейс IPropertyMap
, скажем object GetValue(string key)
, а затем перепишите GetOrDefault<T>
, чтобы использовать этот метод вместо бесконечных и подверженных ошибкам Type
сравнения:
else
return (T)(object)map.GetValue(key);
Ответ 4
Для справки я обнаружил другой интерфейс, который имеет методы GetType() и GetAsObject(), что позволяет мне интегрировать элементы этих ответов, чтобы сделать это:
public static T GetOrDefault<T>(this IInfosContainer container, string key, T defaultValue)
{
//I just read p273 of C# in Depth, +1 Jon Skeet :)
if (container == null) throw new ArgumentNullException("container");
if (container.Exist(key))
{
if (container.GetType(key) != typeof(T))
throw new ArgumentOutOfRangeException("key",
"Key exists, but not same type as defaultValue parameter");
else
return (T)container.GetAsObject(key);
}
else
return defaultValue;
}
(Пуристы заметят, что я не из школы "фигурные скобки для одного заявления"...)
Ответ 5
Я не думаю, что это хороший метод. У вас нет способа контролировать, что такое T. Например,
float value = map.GetOrDefault( "blah", 2.0);
не будет компилироваться либо потому, что
Невозможно неявно преобразовать тип "double" в "float". Явное преобразование существует (вам не хватает роли?)
Другая точка отказа - это когда нулевое значение по умолчанию равно null. Эти методы оставляют разработчика во власти компилятора, чтобы решить, что он думает, что он намеревается.
Если вы можете изменить интерфейс, добавьте метод GetObject. Поскольку вы используете метод расширения, я предполагаю, что вы не можете так передать объект, а затем в int. В любом случае измените метод, чтобы он выглядел как
public static void GetOrDefault (эта карта IPropertyMap, строковый ключ, значение ref T) { if (map.Exists(ключ)) { if (typeof (T) == typeof (int)) { value = (T) (объект) map.GetInt(ключ); } value = default (T);//Это просто тонко, потому что я ленив, // добавьте реальный код здесь. } }
и называйте это
PropertyMap map = new PropertyMap();
float value = 2.0f;
map.GetOrDefault("blah", ref value);
Я ненавижу ref params, но я вижу здесь пункт. Реестр является классическим примером того, когда этот метод полезен. Вышеприведенный код заставляет пользователя-разработчика указать тип выходного файла и сохраняет значение по умолчанию концепции.