Вывод аргумента типа Общего метода C#

Есть ли какой-либо способ, которым я могу обобщить определения типа здесь? Идеально, я хотел бы смочь измениться, тип 'testInput' и иметь тест правильно выводят тип во время компиляции.

public static void Run()
{
    var testInput = 3;
    var test = ((Func) Identity).Compose(n => n)(testInput);
    Console.WriteLine(test);
}

public static Func Compose(this Func f, Func g)
{
    return x => f(g(x));
}

public static T Identity (this T value)
{
    return value;
}

Обновление: Я могу указать, что тип функции передал в, Сочиняют, но это все еще указывает тип в строке.

public static void Run()
{
    var testInput = 3;
    var identity = (Func) Identity;
    var test = identity.Compose((int n) => n)(testInput);
    Console.WriteLine(test);
}

Немного контекста; я работаю через Wes Dyer Чудо Монад

8
задан CaptainCasey 13 May 2010 в 23:12
поделиться

4 ответа

Что ж, раз уж я нахожусь на грани того, что сегодня вечером извергаю текст, у меня есть собственный удар. Я должен отметить, что я не эксперт по компилятору C #, я не читал спецификацию (любую из них ... ни на что), и хотя статья, на которую вы ссылались, была действительно интересной, я бы солгал, если бы сказал, что тоже был экспертом в этом вопросе (или даже понимал все на 100%).

Осторожно, я так понимаю ваш вопрос:

Есть ли способ обобщить здесь определения типов?

Я думаю, что краткий ответ - нет. С предоставленной информацией просто недостаточно информации для части вывода типа компилятора C #, чтобы вывести достаточно информации из использования различных переменных.

Как показывают другие ответы здесь, это можно упростить. Вы можете использовать @ Lee's IdentityFunc , чтобы разрешить вывод типа с помощью var identity . Однако даже с этим добавлением с помощью вашего примера кода по-прежнему невозможно вывести все переменные типа Compose .

Представьте себе следующую ситуацию:

public static Func<T, V> Compose<T, U, V>(this Func<U, V> f, Func<T, U> g)
{
    return x => f(g(x));
}

public static T Identity<T> (this T value)
{
    return value;
}

public static Func<T, T> IdentityFunc<T>(this T value)
{
    return (Func<T, T>)Identity;
}

и

public static void Run()
{
    var a = 3; // a is int
    var i = a.IdentityFunc(); // i is Func<int, int>;
    var test = i.Compose(n => n)(a) // test is expected to be int
}

Сначала может показаться, что тест легко вывести на int . Однако тип возвращаемого значения i.Compose может быть выведен только постфактум, исходя из его использования. Компилятор C #, очевидно, этого не допустит.

public static void Run()
{
    var a = 3; // a is int
    var i = a.IdentityFunc(); // i is Func<int, int>;
    var c = i.Compose(n => n) // c is Func<T, int> - T cannot be resolved without knowledge of n
    var test = c(a); // ideally have type inference infer c (Func<T, int>) as Func<int, int>
}

В этом примере при использовании c с a компилятор должен ретроспективно вывести тип возвращаемого значения вызова i.Compose (n => n) должно быть Func .Очевидно, что в компиляторе C # это невозможно. Уберите вызов c (a) , и компилятор не будет знать об использовании c , что устранит любую возможность вывести T (не то, что все равно может). Возможно, более продвинутая система вывода типов сможет сделать такой вывод на основе использования общего возврата (возможно, F # - еще одна тема, в которой я не эксперт).

Поскольку Уэс Дайер не описывает конкретного использования этого конкретного примера, неизвестно, есть ли какая-то другая магия, которую он использует, чтобы учесть степень вывода типов, которую вы пытаетесь достичь.

Более квалифицированные люди, такие как Эрик Липперт , могли бы предоставить вам гораздо больший уровень детализации (и техническую точность / острота зрения). Я прочитал отличный ответ, который он написал здесь на вопрос о выводе типов, но не могу его найти. В его блоге есть много полезной информации. Вы можете попробовать связаться с ним, если вам интересно. Кроме того, его ответ на этот вопрос здесь обсуждает монады (и, в конечном итоге, ссылается на статью Уэса Дайера), но вам может быть интересно его прочитать: Монада на простом английском языке? (Для программиста ООП без опыта программирования FP)

3
ответ дан 5 December 2019 в 21:17
поделиться

Вы можете написать метод расширения для возврата функции Identity для типа:

public static Func<T, T> IdentityFunc<T>(this T value)
{
    return (Func<T, T>)Identity;
}

(или просто return v => value; )

Тогда ваш тест станет

var testInput = 3; 
var identity = testInput.IdentityFunc();
test = identity.Compose((int n) => n)(testInput);
2
ответ дан 5 December 2019 в 21:17
поделиться

Я не думаю, что вы сможете достичь своего идеала; Вывод типа C # так не работает.

Возможно, вам понравится F #.

1
ответ дан 5 December 2019 в 21:17
поделиться

Самое близкое, что я могу найти, - это явно ввести параметр для лямбда n => n:

var test = ((Func<int, int>)Identity).Compose((int n) => n)(testInput);
1
ответ дан 5 December 2019 в 21:17
поделиться
Другие вопросы по тегам:

Похожие вопросы: