2010-10-12 82 views
1
using System; 

static class Utility<T, TReturn> 
{ 
public static TReturn Change(T arg) 
{ 
    // is there any solution to do this type casting but without dynamic keyword? 
    return (TReturn)(arg as dynamic); 
} 
} 


    class Program 
{ 
    static void Main(string[] args) 
{ 
    int i = 100; 

    try 
    { 
     short s = Utility<int,short>.Change(i); 
     Console.WriteLine(s); 
    } 
    catch (Exception ex) 
    { 
     Console.WriteLine(ex); 
    } 
} 
} 
+0

除了演員之外,你還想達到什麼目的?你是否想避免爲上面的例子寫'(short)i'? – shahkalpesh 2010-10-12 03:59:18

+0

這只是一個簡單的例子。 – xport 2010-10-12 04:03:12

回答

3

絕對如此。在這裏,你去...

static class Utility<T, TReturn> { 
     public static TReturn Change(T arg) { 
      return (TReturn)Convert.ChangeType(arg, typeof(TReturn)); 
     } 
    } 
+0

哇,它的作品! – xport 2010-10-12 03:59:40

+0

@ mattmc3,相比動態,哪一個更好? – xport 2010-10-12 04:08:44

+0

恕我直言,你應該儘可能避免「動態」。動態類型在.Net 4中是新增的,並且刪除了編譯時類型檢查,如果您習慣於輸入安全性,則會使其成爲危險地區。 – 2010-10-12 05:38:24

0
static class Utility<T, TReturn> 
    where T : TReturn 
{ 
    public static TReturn Change(T arg) 
    { 
     return (TReturn) arg; 
    } 
} 
+0

您的代碼不適用於提問者提供的代碼。 'int'不是'short'。提問者正在尋找一種類型轉換,而不僅僅是一個簡單的轉換。 – mattmc3 2010-10-12 04:05:32

0

您可以添加約束T : TReturn。但是這不適用於您的值類型:

using System; 

static class Utility<T, TReturn> 
    where T : TReturn 
{ 
    public static TReturn Change(T arg) 
    { 
     return (TReturn)(arg); 
    } 
} 


class Program 
{ 
    static void Main(string[] args) 
    { 
     string i = "100"; 

     try 
     { 
      object s = Utility<string, object>.Change(i); 
      Console.WriteLine(s); 
     } 
     catch (Exception ex) 
     { 
      Console.WriteLine(ex); 
     } 
    } 
} 
0

即使簡單:

static class Utility<T, TReturn> 
{ 
    static TReturn Change(T arg, Converter<TReturn, T> convert) 
    { 
     System.Diagnostics.Debug.Assert(arg != null); 
     return convert(arg); 
    } 
} 

或者試試下面的,如果你不能接受一個自定義轉換器的委託:

static class Utility<T, TReturn> 
{ 
    static TReturn Change(T arg) 
    { 
     return (TReturn) System.ComponentModel.TypeDescriptor.GetConverter(typeof(T)).ConvertTo(arg, typeof(TReturn)); 
    } 
} 
1

使用Convert.ChangeType(object,Type)的解決方案是不與使用動態調度的方法一樣靈活。主要是因爲很少有類型實現IConvertible。例如,假設你想從一個值類型轉換爲它的可空值類型,這會拋出一個異常。 下面是一個例子::

Converter<short, short?> test1 = @short =>(short?)Convert.ChangeType(@short, typeof(short?)); 
test1(5); 

這是不通用的代碼,但它仍然證明了它會炸掉點。更糟糕的是,在這種情況下,簡單的演員實際上工作。你真正需要的是一個「智能」轉換器。

首先,我們需要定義您願意接受的懲罰。 如果您準備好調用DLR,這可能是您最安全的選擇。該代碼幾乎保證做正確的事情。由於呼叫站點被緩存,如果調用足夠的話,它實際上會表現得非常好。

我不會假裝沒有簡單的解決方案。你最好的選擇是開始考慮限制功能的範圍或範圍。例如,如果您將TResult的限制添加爲IConvertible,那麼您不必擔心可空。儘管如此,它仍然會炸燬Enums。

你可以做的是建立一個轉換器委託來處理返回類型的工作。然後按返回類型緩存它。您將始終禁用值類型,但其內存密集程度要低很多,因此必須緩存該方法調用的每個配對的委託。該邏輯基本上必須處理幾種情況::

If(TReturn.IsValueType) 
{ 
     If(TReturn.IsEnum) Cast to Enum base type using Convert.To(base), then to Enum. 
     If(TReturn.IsNullable<>) Cast to Nullable<> generic type using Convert.To(base), then to Nullable<T> 
     If(TReturn.ISNullableEnum) Cast to Enum base type using Convert.To(base), then to Nullable enum. 
     Otherwise, just call Convert.To(TReturn) if that method exists. 
} 
If it's Iconvertible try calling Convert.ChangeType(object,Type) and cast as TReturn. 
If no method has been found yet, try doing an explicit cast TReturn. 

這對實現轉換運算符的代碼仍然失敗,這不是IConvertible。但它非常接近你想要的。要添加對轉換運算符的支持,您必須爲每個TInput,TReturn緩存不同的委託。這是一個更大的混亂,因爲你現在開始擔心在某些情況下試圖不加框,在那裏它更容易調用拳擊方法,並且需要很多工作才能得到正確的結果。除非這是必須成爲代碼庫的一部分,否則我會避免使用這種清理工具,return (TReturn)(value as dynamic)

我爲我的數據庫層做了這樣的事情,我目前正在嘗試用更簡單和不那麼痛苦的動態代碼替換.NET 3.5代碼。 上述可能會比DLR代碼略微(並且略微)執行得更好,並且不會像DLR呼叫那樣安全或穩健。

+0

謝謝。很好的解釋。 – xport 2010-10-12 05:50:10