Общий метод принимает KeyValuePair.Как переадресовать вызов на правильную перегрузку, используя тип ключа? - PullRequest
0 голосов
/ 19 сентября 2018
 class Program
    {
        static void Main(string[] args)
        {

        }

        void DoSomething(int a)
        {
            Console.WriteLine("int");
        }
        void DoSomething(char a)
        {
            Console.WriteLine("char");
        }
        void DoSomething<T, U>(KeyValuePair<T, U> a)
        {
            DoSomething(a.Key);
        }
    }

У меня есть сторонняя сборка, которая имеет огромную структуру с множеством элементов разных типов.Мне нужно взять некоторые значения из этой структуры и обработать их.Поэтому я пишу перегрузки для разных типов.Я знаю, что мне нужно делать с целыми числами, с символами и т. Д. И если какое-то значение является парой ключ-значение, я знаю, что мне нужно обрабатывать только ключ так же, как и примитивное значение.Код выше - моя неудачная попытка сделать это.Проблема в том, что компилятор C # жалуется, что он не может определить, какую перегрузку вызывать для a.Key, потому что, в отличие от C ++, где он будет создавать экземпляр шаблона и затем автоматически точно знать, какую перегрузку вызвать или завершиться с ошибкой компилятора, C # не делает 'не делайте этого.

Есть ли что-то базовое, что я упускаю, и если нет, то какова обычная идиома C # для решения проблемы дизайна, с которой я сталкиваюсь?

Ответы [ 4 ]

0 голосов
/ 19 сентября 2018

Начиная с C # 7.1, для этого можно использовать оператор is с выходной переменной.Это все еще имеет недостаток dynamic в том, что выдаваемая ошибка будет ошибкой времени выполнения по сравнению с ошибкой времени компиляции, но это устраняет накладные расходы CLR на использование динамического.

private void DoSomething<T, U>(KeyValuePair<T, U> a)
{
    if (a.Key is int keyInt)
    {
        DoSomething(keyInt);
    }
    else if (a.Key is char keyChar)
    {
        DoSomething(keyChar);
    }
    else
    {
        throw new Exception("Unable to DoSomething to a.Key");
    }
}

И то же самое, но красивее, потому что switch оператор.

private void DoSomething<T, U>(KeyValuePair<T, U> a)
{
    switch (a.Key)
    {
        case int keyInt:
            DoSomething(keyInt);
            break;
        case char keyChar:
            DoSomething(keyChar);
            break;
        default:
            throw new Exception("Unable to DoSomething to a.Key");
    }
}
0 голосов
/ 19 сентября 2018

Один хакерский способ - привести a.Key к динамическому:

DoSomething((dynamic)a.Key);

Правильная перегрузка будет определена во время выполнения.Если нет соответствующих перегрузок, вы получите исключение.

0 голосов
/ 19 сентября 2018

Проблема, например, в подходе dynamic заключается в том, что он дает ошибку времени выполнения.Наличие универсального кода, который компилируется, но выдает ошибку времени выполнения из-за несоответствия типов, всегда кажется мне «ложью» - если вы не можете выразить его как ограничение параметра типа и если компилятор не справился с компиляцией, вы не должны писать этот универсальный метод.

Вместо этого я бы поставил два метода:

    void DoSomething<U>(KeyValuePair<int, U> a)
    {
        DoSomething(a.Key);
    }
    void DoSomething<U>(KeyValuePair<char, U> a)
    {
        DoSomething(a.Key);
    }

Теперь вы можете предоставить только KeyValuePair s, с которыми мы знаем, как правильно обращаться, и все остальное возвращается к генерацииошибки времениЯ предпочитаю небольшую избыточность 1 , чтобы получить ошибку во время компиляции.


Конечно, в приведенном выше примере полезность этих методов несколько сомнительна, но в большей базе кодавы все еще можете абстрагировать некоторые функциональные возможности в частный универсальный метод, к которому относятся оба этих DoSomething метода.


1 И, конечно,в исходном коде есть только «кажущаяся» избыточность.В скомпилированном IL для этих методов ссылки на другие методы DoSomething отличаются, так как разрешение перегрузки даст другие результаты.

0 голосов
/ 19 сентября 2018

Если вы на 100% уверены, что T будет int или char - тогда dynamic - это один из подходов, который следует учитывать:

static void DoSomething<T, U>(KeyValuePair<T, U> a)
{
    dynamic bob = a.Key;
    DoSomething(bob);
}
...