[英]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.