在C#中创建通用方法


84

我正在尝试将一堆类似的方法组合成一个通用方法。我有几种返回查询字符串值的方法,如果该查询字符串不存在或格式不正确,则返回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;
}

他正在移植一堆实现,因此调用旧功能,记住结果,调用新功能,记住结果,进行比较。现在,用一堆随机输入将其执行100次,瞧!
Hamish Grubijan 2010年

抱歉,在这种情况下,我仍然不知道该如何应用。我仍在尝试使该功能正常工作。
Mike Cole 2010年

查看答案,我有些困惑:您的呼叫者是使用int还是int参数化?作为T?

在我看来,您应该允许该方法引发异常,而不是在内部进行处理。也许仅仅是我一个人,但是可能有人会感到困惑,为什么他们的呼叫总是返回默认值,因为他们没有看到ChangeType失败时生成的异常。
2016年

Answers:


64

如果您指定要返回的默认值而不是使用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

缺点是您需要魔术值来表示无效/缺少查询字符串值。


是的,这似乎比依赖整数的默认值更可行。我会牢记这一点。我仍然希望我的原始函数适用于所有类型,尽管我可能会诉诸使用非泛型函数。
Mike Cole 2010年

为什么不为一个无效整数不返回零以外的东西呢?您可以返回任何您想要的不是有效值或已经具有特殊用途的值,例如null。您甚至可以创建自己的名为InvalidInteger或其他类型的类型。您为错误的查询字符串返回null,对吗?您也可以为无效的整数返回该值,因此null表示简单的“某事不好,我对您没有任何价值”,并且可能通过引用该函数传递了reasonCode?
Dan Csharpster 2014年

1
如何获取以下内容的值:long ? test默认值应为null
Arshad 2014年

16

我知道我知道但是

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

4
Try-pattern应熟知的任何.NET开发人员。如果你问我,这不是一件坏事。在F#或NET 4.0中,您将使用Option(或Choice)
Christian Klauser

6
如果没有其他原因,我会尽量避免使用它,因为我讨厌必须“预声明”该输出变量,尤其是在从未使用过该变量的情况下,这本来是本来很好的代码。
杰伊,2010年

实际上,这是解决问题的最简单方法-定义一个上述功能+两个使用该功能的助手(这些将是4个衬板)。
greenoldman 2010年

1
我讨厌与Jay所说的Try模式相同的原因。如果可能的话,我希望有一个通用函数,这是我最初的目标。
Mike Cole 2010年

11
做或者不做,不可以尝试!<Yoda>
兔子

12

那这个呢?将返回类型从更改TNullable<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;
        }

错误:类型“ T”必须为非空值类型,才能在通用类型或方法“ System.Nullable <T>”中用作参数“ T”。
Mike Cole 2010年

您还需要指定where T : struct
亚伦诺特,2010年

@迈克C:您应该不会出现相同的错误。编辑后的代码肯定可以编译。
亚罗诺(Aaronaught)2010年

是的,现在知道了。那么,当我想为String类型调用此方法时会发生什么?它不会像现在这样接受它。
Mike Cole 2010年

@MikeC,不要认为这是可能的,因为这string是一种nullable价值
Graviton 2010年

5

您可以使用某种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;
    }
}

您的方法如下所示:

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

4

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

0

我喜欢从这样的类开始:类设置{public int X {get; set;} public string Y {get; 组; } //根据需要重复

 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多个项目中效果很好。您可以使用许多其他解析解决方案之一来解析值。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.