在 C # 中创建一个泛型方法

我正在尝试将许多类似的方法组合成一个通用的方法。我有几个方法可以返回查询字符串的值,如果查询字符串不存在或格式不正确,则返回 null。如果所有类型都是本机为空的,那么这就很容易了,但是对于整数和日期,我必须使用可为空的泛型类型。

这是我现在拥有的。但是,如果一个数值无效,它将返回一个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;
}
294885 次浏览

What about this? Change the return type from T to Nullable<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;
}

I know, I know, but...

public static bool TryGetQueryString<T>(string key, out T queryString)

Convert.ChangeType() doesn't correctly handle nullable types or enumerations in .NET 2.0 BCL (I think it's fixed for BCL 4.0 though). Rather than make the outer implementation more complex, make the converter do more work for you. Here's an implementation I use:

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);
}
}

Then your implementation of GetQueryString<T> can be:

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;
}

I like to start with a class like this class settings { public int X {get;set;} public string Y { get; set; } // repeat as necessary

 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


}

This has worked well on 100's of projects. You can use one of the many other parsing solutions to parse values.

You can use sort of Maybe monad (though I'd prefer Jay's answer)

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;
}
}

Your method would look like:

    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>();
}

What if you specified the default value to return, instead of using default(T)?

public static T GetQueryString<T>(string key, T defaultValue) {...}

It makes calling it easier too:

var intValue = GetQueryString("intParm", Int32.MinValue);
var strValue = GetQueryString("strParm", "");
var dtmValue = GetQueryString("dtmPatm", DateTime.Now); // eg use today's date if not specified

The downside being you need magic values to denote invalid/missing querystring values.