簡體   English   中英

將泛型函數作為參數傳遞

[英]Passing a generic function as a parameter

我知道我正在做的事情可以用不同的方式完成,但我對事情的運作方式很好奇。 以下是一個不編譯的簡化代碼,但它應該顯示我的目標。

private void Execute()
{
    GeneralizedFunction("1", "2", i => Transform(i));
}

void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction)
{
    A result1 = aAction(aStringA);
    B result2 = aAction(aStringB);
    // Do something with A and B here
}

T Transform<T>(string aString)
{
    return default(T);
}

Transform是從字符串到某個對象的通用轉換(想想反序列化)。 GeneralizedFunction使用兩個轉換特化:一個用於類型A,一個用於類型B.我知道我可以通過許多其他方式執行此操作(例如通過引入對象類型的參數),但我正在尋找解釋是否有可能或不可能使用泛型/ lambdas。 如果Transform在作為參數傳遞給GeneralizedFunction之前是專用的,那么這是不可能的。 那么問題是為什么這種可能性受到限制。

這個答案並沒有解釋為什么 ,只是如何解決這個限制。

您可以傳遞具有此類函數的對象,而不是傳遞實際函數:

interface IGenericFunc
{
    TResult Call<TArg,TResult>(TArg arg);
}

// ... in some class:

void Test(IGenericFunc genericFunc)
{
    // for example's sake only:
    int x = genericFunc.Call<String, int>("string");
    object y = genericFunc.Call<double, object>(2.3);
}

對於您的特定用例,可以簡化為:

interface IDeserializerFunc
{
    T Call<T>(string arg);
}

// ... in some class:
void Test(IDeserializerFunc deserializer)
{
    int x = deserializer.Call<int>("3");
    double y = deserializer.Call<double>("3.2");
}

您要求做的事情不可能單獨使用泛型。 編譯器需要生成Transform函數的兩個類型版本:一個返回類型A ,一個返回類型B 編譯器無法知道在編譯時生成它; 只有運行代碼才能知道A和B是必需的。

解決它的一種方法是傳遞兩個版本:

private void Execute()
{
    GeneralizedFunction("1", "2", i => Transform<A>(i), i => Transform<B>(i));
}

void GeneralizedFunction(string aStringA, string aStringB, Func<string, A> aAction,  Func<string, B> bAction)
{
    A result1 = aAction(aStringA);
    B result2 = bAction(aStringB);
}

在這種情況下,編譯器確切地知道它需要生成什么。

請嘗試以下簽名:

void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction)

(注意,GeneralizedFunction必須是通用的;編譯器會在調用方法時自動猜測類型參數)。

似乎答案是“不”。

直接調用Transform ,必須指定一個類型參數:

int i = Transform<int>("");

所以假設,如果你可以像你想要的那樣傳遞一個不完整構造的泛型函數,你還需要指定類型參數:

void GeneralizedFunction(string aStringA, string aStringB, Func<string, T> aAction)
{
    A result1 = aAction<A>(aStringA);
    B result2 = aAction<B>(aStringB);
    // Do something with A and B here
}

所以在我看來,如果C#有這樣的語法,你可以假設這樣做。

但是用例是什么? 除了將字符串轉換為任意類型的默認值之外,我認為沒有太多用處。 你怎么能定義一個函數,它可以使用相同的一系列語句在兩種不同類型中的任何一種中提供有意義的結果?

編輯

分析為什么不可能:

在代碼中使用lambda表達式時,它將被編譯為委托或表達式樹; 在這種情況下,它是一個代表。 您不能擁有“開放”泛型類型的實例; 換句話說,要從泛型類型創建對象,必須指定所有類型參數。 換句話說,如果沒有為其所有類型參數提供參數,就無法擁有委托實例。

C#編譯器的一個有用功能是隱式方法組轉換,其中方法的名稱(“方法組”)可以隱式轉換為表示該方法的一個重載的委托類型。 類似地,編譯器隱式地將lambda表達式轉換為委托類型。 在這兩種情況下,編譯器都會發出代碼來創建委托類型的實例(在這種情況下,將其傳遞給函數)。 但是該委托類型的實例仍然需要為每個類型參數設置一個類型參數。

要將泛型函數作為泛型函數傳遞,似乎編譯器需要能夠將方法組lambda表達式傳遞給方法而不進行轉換 ,因此aAction參數將以某種方式具有“方法組”類型或“lambda表達。” 然后,隱式轉換為委托類型可能發生在調用站點A result1 = aAction<A>(aStringA); B result2 = aAction<B>(aStringB); 當然,在這一點上,我們很好地融入了反事實和假設的世界。

我在午餐時提出的解決方案就是這個,假設一個函數Deserialize<T> ,它接受一個包含序列化數據的字符串並返回一個T類型的對象:

void GeneralizedFunction<T>(string aStringA, string aStringB, Func<T, string> stringGetter)
{
    A result1 = Deserialize<A>(stringGetter(aStringA));
    B result2 = Deserialize<B>(stringGetter(aStringB));
}

void Example(string serializedA, string serializedB, string pathToA, string pathToB, FileInfo a, FileInfo b)
{
    GeneralizedFunction(serializedA, serializedB, s => s);
    GeneralizedFunction(pathToA, pathToB, File.ReadAllText);
    GeneralizedFunction(a, b, fi => File.ReadAllText(fi.FullName));
}
void GeneralizedFunction<T>(string aStringA, string aStringB, Func<string, T> aAction)
{
    A result1 = aAction(aStringA);
    B result2 = aAction(aStringB);
}

T Transform<T>(string aString)
{
    return default(T);
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM