Эмуляция шаблонов со значениями параметров в C # - PullRequest
4 голосов
/ 15 апреля 2019

Есть ли способ, которым я могу эмулировать параметры шаблона значения C ++ в C #?

template<bool flag>
void Method()
{
     // Do some work
     if constexpr(flag)
     {
          // Do something specific
     }
     // Do some more work
}

, чтобы он генерировал две версии метода, которые можно вызывать так:

Method<false>();
Method<true>();

Это из соображений производительности, поэтому лучше не делать дополнительных вызовов внутри Method.Method является критичной для производительности частью, и ее называют миллиардами раз, поэтому важно выжать из нее каждый цикл ЦП.

С другой стороны, он имеет довольно сложную логику, поэтому я бы предпочел неу меня есть две копии.

Я думаю, что я мог бы сделать что-то с дженериками, но это не было бы лучшим вариантом для производительности.Так что теперь я могу думать только о том, чтобы создать для него какой-то генератор шаблонного кода.А может есть другие варианты?Может быть, возможно скомпилировать две версии метода с использованием Roslyn, чтобы он создавал две оптимизированные версии этого метода с конкретными аргументами?

void Method(bool flag)
{
     // Do some work
     if (flag)
     {
          // Do something specific
     }
     // Do some more work
}

Чтобы я мог скомпилировать его в:

void Method_false(false)
{
     // Do some work
     // Do some more work
}

и до:

void Method_true(true)
{
     // Do some work
     // Do something specific
     // Do some more work
}

Возможно ли это вообще?

Ответы [ 2 ]

2 голосов
/ 15 апреля 2019

Метапрограммирование невозможно в C #.Ищите возможные альтернативы в форме инструментов преобразования кода в следующем вопросе SO: Возможно ли метапрограммирование в C #?

1 голос
/ 15 апреля 2019

Вы не можете сделать это непосредственно из языка, однако это возможно , потому что JIT достаточно умен.Создайте интерфейс и две структуры:

public interface IBool
{
    bool IsTrue();
}

public struct True : IBool
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsTrue()
    {
        return true;
    }
}

public struct False : IBool
{
    [MethodImpl(MethodImplOptions.AggressiveInlining)]
    public bool IsTrue()
    {
        return false;
    }
}

Обратите внимание, что реализации IsTrue помечены атрибутом MethodImpl.Теперь переписайте ваш Method как универсальный:

public static void Method<T>(ref T flag) where T : struct, IBool
{
    if (flag.IsTrue())
    {
        Console.WriteLine(42);
    }
    else
    {
        Console.WriteLine(24);
    }
}

Важная вещь: T имеет ограничение struct, так что JIT знает, что нет никаких унаследованных типов от T и никто не может переопределить возвращаемое значениеIsTrue.Кроме того, поскольку этот метод будет встроенным, JIT заменит вызов IsTrue константой.И когда у вас есть

if (false)
{
    // some code
}

, это означает, что весь блок кода будет удален.В результате JIT создаст две реализации Method: первая с содержимым раздела if и вторая с содержимым раздела else.Вы можете проверить это в окне разборки:

Two calls with different target address

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...