2011-01-19 21 views
5

Przenoszę aplikację C++ do C# i uruchamiam szablony. Czytałem trochę na ten temat i rozumiem, że niektóre szablony są podobne do generycznych .Net. Przeczytałem SO answer dla tej sprawy, która ładnie podsumowała to.Przenoszenie C++ do C# - szablony

Jednak niektóre zastosowania szablonu C++ nie wydają się być bezpośrednio związane z rodzajami. W poniższym przykładzie z artykułu Wikipedii Template metaprogramming szablon wydaje się akceptować wartość, a nie typ. Nie jestem całkiem pewien, jak to byłoby przeniesione do 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 
} 

Oczywiście dla tego przykładu mogę zrobić:

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

ale wydaje mi się być refaktoryzacji do funkcji, a nie port semantycznie podobny kod.

+0

Dlaczego używałbyś klasycznej funkcji rekursywnej jako generics C#? Spróbuj przeczytać ten artykuł http://msdn.microsoft.com/en-us/library/bb549151.aspx dla korzystania z Func nemke

+1

Najprawdopodobniej problem nie będzie związany ze stałymi (vs. typami) jako parametrami do szablonów, ale z specjalizacje i częściowe specjalizacje klas lub funkcji, jeśli są one aktualnie używane w bazie kodu. Bez jakiegoś szczególnego przykładu odpowiedź będzie musiała być szeroka i prawdopodobnie mało przydatna. Głosuję za zamknięciem z tego powodu, nie krępuj się dodawać szczegółowe pytania dotyczące jakiegoś fragmentu kodu, w którym masz problemy z konwersją. –

+1

@nemke - W tym przykładzie szablon Factorial rozwija się w czasie kompilacji - w ten sposób żadna praca nie jest wykonywana w czasie wykonywania. OP chce przenieść kod bez konieczności tłumaczenia zbyt wiele na różne typy wywołań C# - wolałby więc, jeśli to możliwe, rozwiązanie generyczne. Niestety tak nie jest. @David - bardzo dobry punkt też –

Odpowiedz

5

Niestety .Net rodzajowych może przyjąć tylko typy. Szablony C++ pobierają inne wartości, które są uznawane za wyrażenia stałe przez kompilator, ponieważ są one faktycznie tylko makrami, które rozszerzają się na więcej kodu.

Oznacza to, że najlepiej będzie, jeśli zamienisz kod na wywołanie metody. Można zrobić wywołanie metoda zwraca typ z .Value własności (po swoim przykładzie) utrzymując w ten sposób przeniesiony kodu podobnego do szablonu:

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

Istnieje dodatkowy punkt, w którym kompilatory C# i C++ są zasadniczo różne, ponieważ C++ jest zaśmiecony funkcjonalnością, która pozwala na pisanie kodu w czasie kompilacji (makra i szablony), podczas gdy zespół C# celowo trzymał się z dala od tego - powołując się na czytelność jako przyczynę . C# może zbliżać się do niego - ale nigdy nie będzie to samo. –

+0

+1 Generics w języku C# to tylko wygoda, zgodnie z linkiem MSDN wszystko jest robione dynamicznie, tak więc wszystko, co zyskujesz, to sprawdzanie typu kompilacji (co jest wystarczająco dobre), ale nie obliczenia kompilacji (co jest przykład robi). – MatiasFG

+0

@MatiasFG - dokładnie ten punkt - generics są kompilowane dynamicznie, ale sprawdzanie typu dla ich kodu odbywa się w czasie kompilacji typu ogólnego. Tęsknię za brakiem funkcji szablonu w C#, gdy zacząłem tworzyć szablonowe programowanie w C++ tuż przed tym jak zostałem pełnoetatowym programistą C# w pełnym wymiarze czasu - jednak generics są bardzo fajne i cieszę się, że je mamy! –

3

Spójrz na tego artykułu różnice między C# rodzajowych i C++ szablony:

myślę, że przykład jest tam zawarte.

MSDN Link

+0

Dzięki, to wyklucza ten przypadek: "C# nie pozwala na parametry szablonu non-type, takie jak szablon C {}.". Czy refaktoryzacja funkcji, jak to zrobiłem w powyższym pytaniu, jest najlepszym sposobem na przeniesienie tego rodzaju szablonów? –

+1

W powyższym przypadku refactoring do metody jest prawdopodobnie najlepszą rzeczą do zrobienia. Ale każda sytuacja musi być rozpatrywana indywidualnie dla każdego przypadku. Czasami możesz/potrzebujesz podać wartość konstruktorowi klasy. –

+1

Dobrą rzeczą jest to, że C# nie wymaga stałych czasowych kompilacji, jak C++ - na przykład przy deklarowaniu tablicy--, więc obliczenie czasu działania w większości przypadków przyniesie oczekiwany wynik. –

1

Krótka odpowiedź jest taka, że ​​nie wszystko, co można zrobić w C++ szablony można wykonać w generycznych elementach C#. W przypadku szablonów, które akceptują wartości inne niż typ, każda sytuacja będzie musiała zostać rozpatrzona i odpowiednio uwzględniona w każdym indywidualnym przypadku.

0

To jest tak blisko, jak mogłem pomyśleć:

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))); 
     } 
    } 

Problemem jest to, że nie można określić rodzajowych i ograniczyć je do ValueTypes. To zadziała dla bajtu, Int16 i Int32. Również dla małych wartości Int64.

+0

Aby wyjaśnić, problem polega na tym, że nie można ograniczyć do wartości numerycznych. Możesz umieścić tam pewne ograniczenia dla struct i IComparable, ale to nie wystarczy, aby określić numeryczne/nieliczbowe. –

5

W poniższym przykładzie ... szablon wydaje się akceptować wartość, a nie typ.

To nie jest twój największy problem. W rzeczywistości może to teoretycznie zostać rozwiązane w języku C# za pomocą reprezentacji Church numeral lub Peano w oparciu o zagnieżdżone typy generyczne.

Jednak, problem jest, że C# nie zezwala szablonu specjalizacji. Specjalizacja szablonów jest odpowiedzialna w twoim przykładzie za określenie, że silnia 0 ma wartość 1, a nie taką samą jak dla wszystkich innych liczb. C# nie pozwala na to.

Więc nie ma sposobu na określenie przypadku podstawowego w definicji szablonu rekursywnego (ogólnego), a zatem bez rekursji. Generics C# nie są kompletne Turinga, a szablony C++ są.


coś takiego:

class Zero { } 

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

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

operacje na tych liczb Wdrażanie pozostawiamy jako ćwiczenie dla czytelnika.

Powiązane problemy