2014-04-23 94 views
6

爲什麼下面的代碼不能編譯? 如何根據泛型類型是「int」,「bool」,「char」等創建一個泛型方法來調用相應的「BitConverter.GetBytes」重載? 更一般地說,我怎樣才能創建一個基於泛型參數的類型調用非泛型方法的泛型方法?如何使用泛型將參數傳遞給非泛型方法?

using System; 

public class Test 
{ 
    public static void Main() 
    { 
     var f = new Foo(); 
     f.GetBytes(10); // should call BitConverter.GetBytes(int); 
     f.GetBytes(true); // should call BitConverter.GetBytes(bool); 
     f.GetBytes('A'); // should call BitConverter.GetBytes(char); 
    } 
} 

public class Foo 
{ 
    public byte[] GetBytes <TSource> (TSource input) 
    { 
     BitConverter.GetBytes(input); 
    } 
} 
+0

下面的答案回答了我的問題;在保持編譯時類型安全性的同時不會造成運行時性能損失。所以,我只是爲我的方法應該接受的輸入類型創建自己的重載GetBytes方法,然後做一些工作並最終調用相應的BitConverter.GetBytes。 –

回答

8

更一般地,我怎麼可以創建調用基於通用參數的類型非泛型方法泛型方法?

在一般情況下,你不能,除非該方法採用System.Object作爲參數。問題是泛型不僅限於方法調用參數允許的類型。

你能做的最接近的是使用運行時綁定:

public byte[] GetBytes <TSource> (TSource input) 
{ 
    dynamic obj = input; 
    BitConverter.GetBytes(obj); 
} 

這推動該方法結合邏輯運行時,如果沒有適當的方法調用將拋出。

+0

你可以在TSource上添加一些約束來幫助一些,但是如果沒有檢查完全違背通用目的的類型,它當然不會是防彈的。 – Kevin

+3

@Kevin在這種情況下,沒有辦法添加可行的約束,因爲你需要像「是一個布爾或是一個浮點數或是一個整數」,這是不允許的。重載需要支持這種類型的「分支」。 –

+0

我同意......我的意思是像'where TSource:struct'這樣的東西當然不是完美的,但可能稍微好一些。 – Kevin

0

你需要使用反射來做到這一點。

  1. BitConverter靜態類型獲取GetBytes方法組。
  2. 拉出第一個參數類型爲TSource的過載。
  3. 通過Invoke方法調用該特定方法。

如果您不熟悉其中的某些內容,我可以用這些步驟的代碼擴展答案。

編輯:或者只是使用dynamic像其他人一樣建議和保存自己的一些工作。

1

這不起作用的原因是泛型方法仍然可以解析對靜態方法中的方法的調用。由於TSource完全可以是任何類型,因此只能調用BitConverter的方法,該方法需要參數object。由於不存在,編譯失敗。

得到你想要的行爲的唯一方法是使用dynamic

public byte[] GetBytes <TSource> (TSource input) 
{ 
    BitConverter.GetBytes((dynamic)input); 
} 

雖然泛型參數現在是多餘的,你有沒有類型安全。

在這種情況下,您可以創建一些匹配的過載例如

public byte[] GetBytes(bool b) { ... } 
public byte[] GetBytes(int i) { ... } 

或採取Func<T, byte[]>參數和包裝每個BitConverter方法需要例如

public void DoSomething<T>(T input, Func<T, byte[]> f) 
{ 
    byte[] bytes = f(input); 
    //handle bytes 
} 
DoSomething(true, BitConverter.GetBytes); 

這可能會給你更多的靈活性。

1

代碼調用BitConverter.GetBytes時,類型爲TSource,因此調用不能被編譯器靜態綁定。您可以解決此使用動態調用,這意味着它會編譯罰款,然後在運行時得到解決:

… 
public byte[] GetBytes(dynamic input) 
{ 
    return BitConverter.GetBytes(input); 
} 

你會付出性能上的損失,使用動態調用,如果沒有合適的方法來調用可用你會得到一個運行時異常。

0

您的代碼無法編譯,因爲編譯器無法驗證的任何類型將被BitConverter.GetBytes()接受。您可以檢查每個類型單獨和投:

public byte[] GetBytes <TSource> (TSource input) 
{ 
    var t = typeof(TSource); 
    return (t == typeof(int)) ? BitConverter.GetBytes((int) (object) input) 
      : (t == typeof(bool)) ? BitConverter.GetBytes((bool)(object) input) 
      : (t == typeof(char)) ? BitConverter.GetBytes((char)(object) input) 
      : null; 
} 
1

既然有「才」的BitConverter.GetBytes 10個重載,這不是不可能明確地反映出他們全部是這樣的:

public class Foo 
{ 
    public byte[] GetBytes(bool input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(char input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(double input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(float input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(int input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(short input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(long input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(uint input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(ulong input) { return BitConverter.GetBytes(input); } 
    public byte[] GetBytes(ushort input) { return BitConverter.GetBytes(input); } 
} 

這不是一般的(你曾經要求過),並且不能擴展到更復雜的例子,但是如果數字很小,那麼它就是的方法來考慮。

1

如果您願意進行性能測試,您可以使用反射和擴展名稱爲GetBytes。例子....

public static class Extensions 
{ 
    #region Fields 
    public static Type bcType; 
    #endregion 

    #region Constructor 
    static Extensions() 
    { 
     bcType = typeof(BitConverter); 
    } 
    #endregion 
    public static byte[] GetBytes(this object value) 
    { 
     Type typeObj = value.GetType(); 
     MethodInfo miGetBytes = bcType.GetMethod("GetBytes", new Type[] { typeObj }); 
     if (miGetBytes == null) 
      throw new InvalidOperationException("Method: GetBytes on BitConverter does not have an overload accepting one paramter of type: " + typeObj.FullName); 
     byte[] bytesRet = (byte[])miGetBytes.Invoke(null, new object[] { obj }); 
     return bytesRet; 
    } 
} 

所以GetBytes接受一個對象。然後得到它的類型,並根據傳入的對象的類型嘗試從BitConverter獲取MethodInfo。如果它找不到接受該類型作爲參數的重載,則會引發InvalidOperation異常。如果是的話,它會調用它傳遞obj實例作爲值並返回字節數組。

E.g.使用代碼,

//make sure the extensions namespace is defined where this code is run. 
Console.WriteLine(((ushort)255).GetBytes().ToBase64()); 
Console.WriteLine(10.0.GetBytes().ToBase64()); 
Console.WriteLine(((int)2000000000).GetBytes().ToBase64()); 
Console.WriteLine(((short)128).GetBytes().ToBase64()); 
//Below causes an error 
Console.WriteLine("cool".GetBytes().ToBase64()); //because BitConvert.GetBytes has no overload accepting an argument of type string. 
相關問題