[英]Variable generic return type in 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
是什么,但我事先检查它。 无论如何我能做到这一点吗?
我以前做过这些类型的泛型方法。 获得类型推断的最简单方法是提供通用转换器函数。
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
为什么要使用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这样的值类型)会阻止对值进行打包/取消装箱的需要。 这两种方法都会使该方法的执行速度更快一些。
不确定这是否正是您想要的,但如果您先将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);
}
我建议不要每次执行例程时测试类型参数,你应该创建一个类似这样的通用静态类:
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); }
使用这种方法,只需在第一次使用特定类型时进行特殊处理。 之后,可以仅使用单个通用委托调用来执行转换。 一旦可以轻松添加任意数量的类型,甚至允许转换器在运行时注册任何所需类型。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.