2011-01-19 4 views
5

Я портирую приложение C++ на C# и запускаю шаблоны. Я немного прочитал об этом, и я понимаю, что некоторые шаблоны сродни .Net generics. Я прочитал SO answer в этом случае, который хорошо подвел итог.Портирование C++ на C# - шаблоны

Однако некоторые применения шаблонов C++, похоже, не связаны напрямую с дженериками. В приведенном ниже примере из статьи Википедии Template metaprogramming шаблон, похоже, принимает значение, а не тип. Я не совсем уверен, как это будет перенесено на C#?

template <int N> 
struct Factorial 
{ 
    enum { value = N * Factorial<N - 1>::value }; 
}; 

template <> 
struct Factorial<0> 
{ 
    enum { value = 1 }; 
}; 

// Factorial<4>::value == 24 
// Factorial<0>::value == 1 
void foo() 
{ 
    int x = Factorial<4>::value; // == 24 
    int y = Factorial<0>::value; // == 1 
} 

Очевидно, что для этого примера я мог бы сделать:

public int Factorial(int N){ 
    if(N == 0) return 1; 
    return Factorial(N - 1); 
} 

, но это, мне кажется, что рефакторинг к функции, а не порт семантически аналогичный код.

+0

Почему вы используете классическую рекурсивную функцию как C# generics? Попробуйте прочитать эту статью http://msdn.microsoft.com/en-us/library/bb549151.aspx для использования Func nemke

+1

Проблема, скорее всего, не будет связана с константами (против типов) в качестве параметров шаблонов, но с специализации и частичные специализации классов или функций, если они в настоящее время используются в вашей базе кода. Без конкретного примера ответ должен быть широким и, вероятно, не очень полезным. Я голосую, чтобы закрыть по этой причине, не стесняйтесь добавлять конкретные вопросы о какой-то части кода, с которой у вас возникают проблемы с преобразованием. –

+1

@nemke - В этом примере шаблон Factorial расширяется во время компиляции - таким образом, работа во время выполнения не выполняется. OP хочет портировать код, не переведя слишком много на разные типы вызовов C#, поэтому, если возможно, предпочтет решение на основе общего набора. К сожалению, это не так. @David - очень хорошая точка тоже –

ответ

5

К сожалению .Net дженерик могут принимать только тип. Шаблоны C++ принимают другие значения, которые компилятором считаются постоянными выражениями, поскольку они фактически являются просто макросами, которые расширяются до большего количества кода.

Это означает, что ваша идея превратить код в вызов метода - лучшая ставка. Вы можете сделать вызов метода возвращают тип с .Value собственности (следуя вашему примеру), таким образом, сохраняя перенесенного код, аналогичный шаблону:

return Factorial(N-1).Value; 
+0

Есть еще одна мысль о том, что компиляторы C# и C++ принципиально отличаются друг от друга в том, что C++ усеяна функциональностью, позволяющей писать код во время компиляции (макросы и шаблоны), тогда как команда C# сознательно не понимала этого - цитируя читаемость как свою причину , C# может приближаться к нему, но он никогда не будет прежним. –

+0

+1 Дженерики в C# - это просто удобство, в соответствии с ссылкой MSDN все делается динамически в любом случае, поэтому все, что вы получаете, это проверка типа времени компиляции (что достаточно хорошо), но не вычисления времени компиляции (что и есть вы пример делает). – MatiasFG

+0

@MatiasFG - точно точка - генераторы динамически скомпилированы, но проверка типов для их кода выполняется во время компиляции родового типа. Я пропустил отсутствие функциональности шаблона в C#, начав делать мета-программирование шаблонов на C++, прежде чем стать разработчиком C# полный рабочий день - однако дженерики очень круты сами по себе, и я рад, что у нас есть они! –

3

Посмотрите на эту статью для различия между генериков # C и C++ шаблонов:

Я думаю, что ваш пример включен там.

MSDN Link

+0

Спасибо, это исключает этот случай: «C# не разрешает параметры шаблона непигового типа, такие как шаблон C {}.". Является ли рефакторинг функцией, как я сделал в вышеупомянутом вопросе, лучший способ портировать эти типы шаблонов? –

+1

В приведенном выше случае рефакторинг для метода, вероятно, лучше всего подходит. Но каждая ситуация должна решаться на индивидуальной основе. Иногда вам может понадобиться/нужно предоставить значение конструктору класса. –

+1

Хорошо, что C# не требует констант времени компиляции как C++ - например, при объявлении массива - поэтому вычисление времени выполнения в большинстве случаев даст ожидаемый результат. –

1

Короткий ответ заключается в том, не все, что может быть сделано в C++ шаблоны могут быть выполнены в C# generics. В случае шаблонов, которые принимают значения типа non, каждая ситуация должна обрабатываться и соответствующим образом учитываться в каждом конкретном случае.

0

Это так близко, как я мог думать:

public class Factorial<T> 
    where T : IConvertible 
    { 
     public T GetFactorial(T t) 
     { 
      int int32 = Convert.ToInt32(t); 
      if (int32 == 0) 
       return (T) Convert.ChangeType(1, typeof(T)); 
      return GetFactorial((T) Convert.ChangeType(int32-1, typeof(T))); 
     } 
    } 

Проблема заключается в том, вы не можете определить, дженерики и ограничить его ValueTypes. Это будет работать для байта , Int16 и Int32. Также для небольших значений Int64.

+0

Чтобы уточнить, проблема заключается в том, что вы не можете ограничивать числовые значения. Вы можете установить некоторые ограничения для struct и IComparable, но этого недостаточно, чтобы определить числовые/нечисловые. –

5

В приведенном ниже примере ... шаблон, похоже, принимает значение, а не тип.

Это не ваша самая большая проблема. На самом деле это теоретически можно было бы решить в C#, используя представление Church numeral или Peano, основанное на вложенных генерических типах.

Однако, ваша проблема в том, что C# не позволяет специализации шаблона. В вашем примере специализация шаблона отвечает за определение того, что факториал 0 равен 1, а не столько же, сколько и для всех остальных номеров. C# не позволяет это делать.

Таким образом, нет способа указать базовый регистр в определении рекурсивного шаблона (generic) и, следовательно, нет рекурсии. C# generics не являются Тьюрингом полным, тогда как шаблоны C++.


Что-то вроде этого:

class Zero { } 

class Successor<T> : Zero where T : Zero { } 

// one: 
Successor<Zero> 
// two: 
Successor<Successor<Zero>> 
// etc. 

Осуществление операций по этим номерам остается в качестве упражнения для читателя.

Смежные вопросы