2012-07-19 23 views
20

有什么办法让方法返回一个方法中的任何一个泛型类型?例如,我有以下几点:C#中的可变通用返回类型#

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,但我正在事先检查它。无论如何,我可以做这项工作吗?

+0

否,则''只是类型参数。您使用不带括号的类型。 – Femaref 2012-07-19 18:46:57

+0

@Femaref - 好点。问题被撤销。 – 2012-07-19 18:50:30

+0

如果将该值存储为Object,则可以对其进行转换。也可以将该功能键入为动态,这可能会帮助您在没有投射的情况下进行操作。 – MrWednesday 2012-07-19 18:51:30

回答

20

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

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

我认为它不会比这更优雅。 +1。 – Adam 2012-07-19 19:04:05

+0

关于这个的好处是,'T'可以从使用中推断,因为它是'Func键<,>'里面。所以,即使我们必须提供一个额外的参数(即'Func键<,>'代表),我们将不会有显式地指定类型参数'T'。 – 2012-07-19 19:31:49

+1

很多通用转换器已经存在在.NET Framework:'TypeDescriptor.GetTypeConverter(typeof运算(T));' – 2012-07-19 23:54:45

4

为什么你使用type参数作为返回类型呢?这会工作,只是需要投打完电话后:

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这样的值类型)可以防止需要对值进行装箱/取消装箱。这两种方法都会使该方法执行速度更快。

+0

+1您的第二个建议。没有理由使用反射来找出类型,并在语言已经提供重载方法时执行相应的操作,这是一个为您完成这项工作(并且可能更高效)的工具。 – 2012-07-19 18:59:31

+0

是的,放弃泛型并做出明确命名的方法是一个常见的解决方案,并没有那么糟糕。输入'ParseAttributeValueAsString'几乎是那么容易,因为'ParseAttributeValue ',带或不带智能感知。而通用方法已经是一大堆特殊情况了,所以如果你把它拉出到不同的方法上,就不会发生重复。 – 2012-07-19 19:42:32

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"); 
+0

这就是立即解决。总是允许显式地将类型参数转换为'object',然后'object'可以转换为任何东西。但它很丑陋。它涉及到在Int32,Double和其他结构的情况下装箱和拆箱。 – 2012-07-19 19:35:02

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"); 
    } 
+1

如果泛型类型是值类型,那么'ReadSetting'将抛出'NullReferenceException',因为值类型不能为空。这就是为什么把一个对象放在一个对象上是危险的。 – Joshua 2012-07-19 19:02:42

+0

这是真的,但在很多情况下它可能是一个合适的结果。例如,假设这不仅仅是简单的示例代码,用于显示方法,如果调用者试图读取无法解析或未指定的设置,则可能会合理预期发生异常。 – MrWednesday 2012-07-19 19:58:53

1

C++模板,这有些东西会起作用,但前提是每一段代码都在不同的,不同的专业领域。这使得这项工作是未使用的函数模板不编译(或者更准确地说:不完全实例化)的事情,所以实际上的一段代码将是无效的,如果该模板的副本中实例化与不同类型的不上来。

C#是不同的,据我所知没有专业化的仿制药。一种方式来完成你正在尝试做的,而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); 
} 
8

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

请注意,如果您需要处理在不同文化中表达的解析值,则可以使用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); 
} 

这将为很多内置的类型,你可以装饰自定义类型的具有TypeConverterAttribute工作,让他们参与到:

下面的代码将使用默认的文化解析值类型转换游戏。这意味着将来您将能够解析新类型而无需更改ParseAttributeValue的实现。

看到:http://msdn.microsoft.com/en-us/library/system.componentmodel.typeconverter.aspx

0

我会建议,而不是每次执行例行一次测试时类型参数,你应该创建一个通用的静态类是这样的:

 
internal static class ElementParser<T> 
{ 
    public static Func<XElement, string, T> Convert = InitConvert; 

    T DefaultConvert(XElement element, string attribute) 
    { 
    return Default(T); // Or maybe throw exception, or whatever 
    } 

    T InitConvert(XElement element, string attribute) 
    { 
    if (ElementParser<int>.Convert == ElementParser<int>.InitConvert) 
    { // First time here for any type at all 
     Convert = DefaultConvert; // May overwrite this assignment below 
     ElementParser<int>.Convert = 
     (XElement element, string attribute) => 
      Int32.Parse(element.Attribute(attribute).Value); 
     ElementParser<double>.Convert = 
     (XElement element, string attribute) => 
      Int32.Parse(element.Attribute(attribute).Value); 
     // etc. for other types 
    } 
    else // We've done other types, but not this type, and we don't do anything nice for it 
    { 
     Convert = DefaultConvert; 
    } 
    return Convert(element, attribute);  
    } 
} 
public static T ParseAttributeValue(this XElement element, string attribute) 
{ 
    ElementParser<T>.Convert(element, attribute); 
} 

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

+0

为什么不使用内建的TypeConverter类? – 2012-07-19 23:51:39

+0

如果这个特定的班级适合自己的申请,那就太好了。如果需要某个类别不支持的特定行为,上面提供的方法可以很容易地适应任何所需的额外翻译。 – supercat 2012-07-20 05:57:47