Создание универсального метода в C#
Я пытаюсь объединить кучу подобных методов в универсальный метод. У меня есть несколько методов, которые возвращают значение строки запроса или null, если эта строка запроса не существует или не находится в правильном формате. Это было бы достаточно легко, если бы все типы были изначально nullable, но я должен использовать nullable generic type для целых чисел и дат.
вот что у меня сейчас. Однако он будет возвращать 0, если числовое значение недопустимо, и это, к сожалению, является допустимым значение в моих сценариях. Может кто-нибудь мне помочь? Спасибо!
public static T GetQueryString<T>(string key) where T : IConvertible
{
T result = default(T);
if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false)
{
string value = HttpContext.Current.Request.QueryString[key];
try
{
result = (T)Convert.ChangeType(value, typeof(T));
}
catch
{
//Could not convert. Pass back default value...
result = default(T);
}
}
return result;
}
6 ответов:
Что делать, если вы указали значение по умолчанию для возврата, вместо использования default(T)?
public static T GetQueryString<T>(string key, T defaultValue) {...}
это делает вызов его легче тоже:
var intValue = GetQueryString("intParm", Int32.MinValue); var strValue = GetQueryString("strParm", ""); var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified
недостатком является то, что вам нужны магические значения для обозначения недопустимых/отсутствующих значений строки запроса.
как насчет этого? Измените тип возврата с
T
toNullable<T>
public static Nullable<T> GetQueryString<T>(string key) where T : struct, IConvertible { T result = default(T); if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false) { string value = HttpContext.Current.Request.QueryString[key]; try { result = (T)Convert.ChangeType(value, typeof(T)); } catch { //Could not convert. Pass back default value... result = default(T); } } return result; }
Convert.ChangeType()
неправильно обрабатывает типы с нулевым значением или перечисления в .NET 2.0 BCL (я думаю, что это исправлено для BCL 4.0). Вместо того, чтобы усложнять внешнюю реализацию, сделайте конвертер более эффективным для вас. Вот реализация, которую я использую:public static class Converter { public static T ConvertTo<T>(object value) { return ConvertTo(value, default(T)); } public static T ConvertTo<T>(object value, T defaultValue) { if (value == DBNull.Value) { return defaultValue; } return (T) ChangeType(value, typeof(T)); } public static object ChangeType(object value, Type conversionType) { if (conversionType == null) { throw new ArgumentNullException("conversionType"); } // if it's not a nullable type, just pass through the parameters to Convert.ChangeType if (conversionType.IsGenericType && conversionType.GetGenericTypeDefinition().Equals(typeof(Nullable<>))) { // null input returns null output regardless of base type if (value == null) { return null; } // it's a nullable type, and not null, which means it can be converted to its underlying type, // so overwrite the passed-in conversion type with this underlying type conversionType = Nullable.GetUnderlyingType(conversionType); } else if (conversionType.IsEnum) { // strings require Parse method if (value is string) { return Enum.Parse(conversionType, (string) value); } // primitive types can be instantiated using ToObject else if (value is int || value is uint || value is short || value is ushort || value is byte || value is sbyte || value is long || value is ulong) { return Enum.ToObject(conversionType, value); } else { throw new ArgumentException(String.Format("Value cannot be converted to {0} - current type is " + "not supported for enum conversions.", conversionType.FullName)); } } return Convert.ChangeType(value, conversionType); } }
тогда ваша реализация GetQueryString
может быть: public static T GetQueryString<T>(string key) { T result = default(T); string value = HttpContext.Current.Request.QueryString[key]; if (!String.IsNullOrEmpty(value)) { try { result = Converter.ConvertTo<T>(value); } catch { //Could not convert. Pass back default value... result = default(T); } } return result; }
Вы можете использовать что-то вроде монады (хотя я бы предпочел ответ Джея)
public class Maybe<T> { private readonly T _value; public Maybe(T value) { _value = value; IsNothing = false; } public Maybe() { IsNothing = true; } public bool IsNothing { get; private set; } public T Value { get { if (IsNothing) { throw new InvalidOperationException("Value doesn't exist"); } return _value; } } public override bool Equals(object other) { if (IsNothing) { return (other == null); } if (other == null) { return false; } return _value.Equals(other); } public override int GetHashCode() { if (IsNothing) { return 0; } return _value.GetHashCode(); } public override string ToString() { if (IsNothing) { return ""; } return _value.ToString(); } public static implicit operator Maybe<T>(T value) { return new Maybe<T>(value); } public static explicit operator T(Maybe<T> value) { return value.Value; } }
ваш метод будет выглядеть так:
public static Maybe<T> GetQueryString<T>(string key) where T : IConvertible { if (String.IsNullOrEmpty(HttpContext.Current.Request.QueryString[key]) == false) { string value = HttpContext.Current.Request.QueryString[key]; try { return (T)Convert.ChangeType(value, typeof(T)); } catch { //Could not convert. Pass back default value... return new Maybe<T>(); } } return new Maybe<T>(); }
Мне нравится начинать с такого класса настройки класса { public int X {get;set;} публичная строка Y { get; set; } // повторите при необходимости
public settings() { this.X = defaultForX; this.Y = defaultForY; // repeat ... } public void Parse(Uri uri) { // parse values from query string. // if you need to distinguish from default vs. specified, add an appropriate property }
Это хорошо работает на 100-х проектов. Для анализа значений можно использовать одно из многих других решений синтаксического анализа.