有没有办法让一个方法从一个方法返回许多泛型类型中的任何一个?例如,我有以下内容:
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
是什么,但我会事先检查它。有没有办法让我做这件事?
我过去做过这些类型的泛型方法。获得类型推断的最简单方法是提供一个通用的转换器函数。
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;
});
.Net已经有了一系列可以使用的优秀字符串转换例程!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
为什么要使用类型参数作为返回类型?这是可行的,只需要在调用后进行强制转换
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等值类型)可以防止对值进行装箱/取消装箱。这两种情况都会使该方法执行得更快。
不确定这是否正是您想要的,但如果您先转换为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");
这里有两种方法…
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");
}
使用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);
}
我建议,与其每次执行例程时都测试类型参数,不如创建一个类似于以下内容的通用静态类:
内部静态类ElementParser<T>{公共静态Func<XElement,字符串,T>Convert=InitConvert;T DefaultConvert(XElement元素,字符串属性){return默认值(T);//或者抛出异常,或者其他什么}T InitConvert(XElement元素,字符串属性){if(ElementParser<int>.Convert=ElementParser<int<.InitnConvert){//第一次出现在任何类型转换=默认转换;//可以覆盖下面的此作业ElementParser<int>。转换=(XElement元素,字符串属性)=>Int32.Parse(element.Attribute(Attribute).Value);ElementParser<加倍>。转换=(XElement元素,字符串属性)=>Int32.Parse(element.Attribute(Attribute).Value);//其他类型的}else//我们做过其他类型的,但没有做这种类型的,我们没有为此做任何好事{Convert=默认转换;}return Convert(元素、属性);}}公共静态T ParseAttributeValue(此XElement元素,字符串属性){ElementParser<T>。转换(元素、属性);}
使用这种方法,只需要在第一次使用特定类型时进行特殊处理。之后,可以仅使用单个通用委托调用来执行转换。Once可以轻松添加任意数量的类型,甚至允许在运行时为任何所需类型注册转换器。