简体   繁体   English

在C#中创建通用方法

[英]Creating a generic method in C#

I am trying to combine a bunch of similar methods into a generic method. 我正在尝试将一堆类似的方法组合成一个通用方法。 I have several methods that return the value of a querystring, or null if that querystring does not exist or is not in the correct format. 我有几种返回查询字符串值的方法,如果该查询字符串不存在或格式不正确,则返回null。 This would be easy enough if all the types were natively nullable, but I have to use the nullable generic type for integers and dates. 如果所有类型本机均可为空,这将很容易,但是我必须对整数和日期使用可为空的泛型类型。

Here's what I have now. 这就是我现在所拥有的。 However, it will pass back a 0 if a numeric value is invalid, and that unfortunately is a valid value in my scenarios. 但是,如果数值无效,它将传回0,但不幸的是,在我的方案中,该值是有效值。 Can somebody help me out? 有人可以帮我吗? Thanks! 谢谢!

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

What if you specified the default value to return, instead of using default(T)? 如果您指定要返回的默认值而不是使用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. 缺点是您需要魔术值来表示无效/缺少查询字符串值。

我知道我知道但是

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

What about this? 那这个呢? Change the return type from T to Nullable<T> 将返回类型从T更改为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;
        }

You can use sort of Maybe monad (though I'd prefer Jay's answer) 您可以使用某种Maybe monad(尽管我希望Jay回答)

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

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). Convert.ChangeType()不能正确处理.NET 2.0 BCL中的可为空的类型或枚举(尽管我认为它对于BCL 4.0是固定的)。 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: 然后,您对GetQueryString <T>的实现可以是:

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; 我喜欢从这样的类开始:类设置{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. 这在100多个项目中效果很好。 You can use one of the many other parsing solutions to parse values. 您可以使用许多其他解析解决方案之一来解析值。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM