21

有没有办法让一个方法从一个方法返回多个泛型类型中的任何一个?例如,我有以下内容:

public static T ParseAttributeValue<T>(this XElement element, string attribute)
    {
        if(typeof(T) == typeof(Int32))
        {
            return Int32.Parse(element.Attribute(attribute).Value);
        }

        if(typeof(T) == typeof(Double))
        {
            return Double.Parse(element.Attribute(attribute).Value);
        }

        if(typeof(T) == typeof(String))
        {
            return element.Attribute(attribute).Value;
        }

        if(typeof(T) == typeof(ItemLookupType))
        {
            return Enum.Parse(typeof(T), element.Attribute(attribute).Value);
        }
    }

(这只是一个非常快速的模型,我知道任何生产代码都需要在空检查等方面更加彻底......)

但是编译器不喜欢它,抱怨Int32不能隐式转换为T(它也不适用于强制转换)。我可以理解。在编译时它无法知道是什么T,但我会事先检查它。无论如何我可以完成这项工作吗?

4

7 回答 7

21

我过去做过这些类型的泛型方法。获得类型推断的最简单方法是提供通用转换器函数。

public static T ParseAttributeValue<T>
          (this XElement element, string attribute, Func<string, T> converter)
{
  string value = element.Attribute(attribute).Value;
  if (String.IsNullOrWhiteSpace(value)) {
    return default(T);
  }

  return converter(value);
}

你可以像下面这样使用它:

int index = element.ParseAttributeValue("index", Convert.ToInt32);
double price = element.ParseAttributeValue("price", Convert.ToDouble);

你甚至可以提供自己的函数并享受世界上所有的乐趣(甚至返回匿名类型):

ItemLookupType lookupType = element.ParseAttributeValue("lookupType",
  value => Enum.Parse(typeof(ItemLookupType), value));

var item = element.ParseAttributeValue("items",
  value => {
    List<string> items = new List<string>();
    items.AddRange(value.Split(new [] { ',' }));
    return items;
  });
于 2012-07-19T18:55:27.027 回答
10

.Net 已经有一堆很棒的字符串转换例程可以使用!ATypeConverter可以为您完成大部分繁重的工作。然后您不必担心为内置类型提供自己的解析实现。

请注意,TypeConverter如果您需要处理以不同文化表达的解析值,则可以使用 API 的语言环境感知版本。

以下代码将使用默认区域性解析值:

using System.ComponentModel;

public static T ParseAttributeValue<T>(this XElement element, string attribute)
{
    var converter = TypeDescriptor.GetConverter(typeof(T));
    if (converter.CanConvertFrom(typeof(string)))
    {
        string value = element.Attribute(attribute).Value;
        return (T)converter.ConvertFromString(value);
    }

    return default(T);
}

这适用于许多内置类型,您可以使用 a 装饰自定义类型,TypeConverterAttribute以允许它们也参与类型转换游戏。这意味着将来您将能够解析新类型,而无需更改ParseAttributeValue.

请参阅:http: //msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

于 2012-07-19T23:03:34.180 回答
4

为什么你完全使用类型参数作为返回类型?这会起作用,只需要在调用后进行强制转换:

public static Object ParseAttributeValue<T>(this XElement element, string attribute)
{
    if(typeof(T) == typeof(Int32))
    {
        return Int32.Parse(element.Attribute(attribute).Value);
    }

    if(typeof(T) == typeof(Double))
    {
        return Double.Parse(element.Attribute(attribute).Value);
    }

    if(typeof(T) == typeof(String))
    {
        return element.Attribute(attribute).Value;
    }

    if(typeof(T) == typeof(ItemLookupType))
    {
        return Enum.Parse(typeof(T), element.Attribute(attribute).Value);
    }
}

或者更好:

public static Int32 ParseAsInt32(this XElement element, string attribute)
{
    return Int32.Parse(element.Attribute(attribute).Value);
}

// etc, repeat for each type

第二种方法还有一个额外的好处,即内联的可能性要高得多,而且它(对于像 Int32 这样的值类型)将避免对值进行装箱/拆箱的需要。这两者都会使该方法执行得更快一些。

于 2012-07-19T18:46:46.653 回答
2

不确定这是否正是您想要的,但是如果您object先转换为然后转换为T

    public static T ParseAttributeValue<T>(this XElement element, string attribute)
    {
        if (typeof(T) == typeof(Int32))
        {
            return (T)(object)Int32.Parse(element.Attribute(attribute).Value);
        }

        if (typeof(T) == typeof(Double))
        {
            return (T)(object)Double.Parse(element.Attribute(attribute).Value);
        }

        if (typeof(T) == typeof(String))
        {
            return (T)(object)element.Attribute(attribute).Value;
        }

        return default(T);
    }

但是,您仍然必须T在编译时提供,调用如下方法:

int value = element.ParseAttributeValue<int>("attribute");
于 2012-07-19T18:55:38.803 回答
2

这里有两种方法...

    static T ReadSetting<T>(string value)
    {
        object valueObj = null;
        if (typeof(T) == typeof(Int32))
            valueObj = Int32.Parse(value);
        return (T)valueObj;
    }
    static dynamic ReadSetting2<T>(string value)
    {
        if (typeof(T) == typeof(Int32))
            return Int32.Parse(value);
        throw new UnsupportedException("Type is unsupported");
    }
    static void Main(string[] args)
    {
        int val1 = ReadSetting<Int32>("2");
        int val2 = ReadSetting2<Int32>("3");
    }
于 2012-07-19T18:55:57.043 回答
1

使用 C++ 模板,这种事情会起作用,但前提是每段代码都处于不同的、单独的专业化中。使这项工作起作用的原因是未编译未使用的函数模板(或更准确地说:未完全实例化),因此如果模板的副本用不同的类型实例化,则一段代码将无效的事实不会过来。

C# 是不同的,AFAIK 没有泛型的专门化。在 C# 的限制范围内完成您正在尝试做的事情的一种方法是创建一个具有更抽象返回类型的函数,并仅使用 ParseAttributeValue 将其强制转换为 T。

所以你会有:

private static Object AbstractParseValue(System.Type t, XElement element, string attribute)

public static T ParseAttributeValue<T>(this XElement element, string attribute)
{
     return (T)AbstractParseValue(typeof(T), element, attribute);
}
于 2012-07-19T18:57:48.190 回答
1

我建议不要在每次执行例程时都测试类型参数,而应该创建一个像这样的通用静态类:

内部静态类 ElementParser<T>
{
  public static Func<XElement, string, T> Convert = InitConvert;

  T DefaultConvert(XElement 元素,字符串属性)
  {
    返回默认值(T);// 或者可能抛出异常,或者其他
  }

  T InitConvert(XElement 元素,字符串属性)
  {
    if (ElementParser<int>.Convert == ElementParser<int>.InitConvert)
    { // 任何类型的第一次
      转换 = 默认转换;// 可能会覆盖下面的这个赋值
      ElementParser<int>.Convert =
        (XElement 元素,字符串属性)=>
          Int32.Parse(element.Attribute(attribute).Value);
      ElementParser<double>.Convert =
        (XElement 元素,字符串属性)=>
          Int32.Parse(element.Attribute(attribute).Value);
      // 其他类型的等等
    }
    else // 我们做了其他类型,但不是这个类型,我们没有为它做任何好事
    {
      转换 = 默认转换;
    }
    返回转换(元素,属性);      
  }
}
public static T ParseAttributeValue(这个 XElement 元素,字符串属性)
{
  ElementParser<T>.Convert(元素, 属性);
}

使用这种方法,只需在第一次使用特定类型时进行特殊处理。之后,可以仅使用单个通用委托调用来执行转换。一旦可以轻松添加任意数量的类型,甚至允许在运行时为任何所需类型注册转换器。

于 2012-07-19T23:23:02.863 回答