2011-01-19 36 views
5

Tôi đang chuyển một ứng dụng C++ sang C# và đã chạy trên các mẫu. Tôi đã đọc lên một chút về những điều này, và tôi hiểu rằng một số mẫu giống như .Net generics. Tôi đọc số SO answer đối với trường hợp này đã tóm tắt nó một cách hợp lý.Porting C++ to C# - templates

Tuy nhiên, một số cách sử dụng khuôn mẫu C++ dường như không liên quan trực tiếp đến Generics. Trong ví dụ dưới đây từ bài viết Template metaprogramming của Wikipedia, mẫu có vẻ chấp nhận một giá trị, chứ không phải là một loại. Tôi không hoàn toàn chắc chắn làm thế nào điều này sẽ được chuyển đến 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 
} 

Rõ ràng ví dụ này tôi có thể làm:

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

nhưng điều này dường như tôi trở thành một refactoring để một chức năng, chứ không phải là một cổng để mã ngữ nghĩa tương tự.

+0

Tại sao bạn sẽ sử dụng chức năng đệ quy cổ điển như một C# generics? Hãy thử đọc bài viết này http://msdn.microsoft.com/en-us/library/bb549151.aspx để sử dụng Func nemke

+1

Vấn đề có thể sẽ không được với hằng số (so với các loại) làm tham số cho mẫu, nhưng với chuyên môn hóa và chuyên môn hóa một phần các lớp hoặc chức năng nếu chúng hiện đang được sử dụng trong cơ sở mã của bạn. Nếu không có một số ví dụ cụ thể, câu trả lời sẽ cần phải rộng và có thể không thực sự hữu ích. Tôi bỏ phiếu để đóng vì lý do đó, cảm thấy tự do để thêm câu hỏi cụ thể về một số đoạn mã mà bạn có vấn đề chuyển đổi. –

+1

@nemke - Trong ví dụ này, mẫu Factorial mở rộng tại thời gian biên dịch - do đó không có công việc nào thực sự được thực hiện trong thời gian chạy. OP muốn chuyển mã mà không cần phải dịch quá nhiều vào các loại C# khác nhau - vì vậy sẽ thích giải pháp dựa trên chung hơn nếu có thể. Thật không may, nó không phải. @David - điểm rất tốt quá –

Trả lời

5

Thật không may .Net generics chỉ có thể chấp nhận các loại. Các mẫu C++ lấy các giá trị khác được coi là biểu thức liên tục bởi trình biên dịch, vì chúng có hiệu quả chỉ các macro mở rộng tới nhiều mã hơn.

Điều này có nghĩa là ý tưởng biến mã thành cuộc gọi phương thức là đặt cược tốt nhất. Bạn có thể thực hiện cuộc gọi phương pháp trả về một kiểu với một tài sản .Value (theo gương của bạn) vì vậy sẽ giữ mã được chuyển tương tự như các mẫu:

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

Có điểm bổ sung là các trình biên dịch C# và C++ về cơ bản khác nhau trong C++ có chức năng cho phép viết mã tại thời gian biên dịch (macro và mẫu) trong khi nhóm C# cố ý không rõ ràng điều này - trích dẫn khả năng đọc là lý do của chúng . C# có thể đang tiến gần hơn đến nó - nhưng nó sẽ không bao giờ giống nhau. –

+0

+1 Generics trong C# chỉ là một tiện lợi, theo liên kết MSDN mọi thứ được thực hiện tự động, vì vậy tất cả các bạn đạt được là kiểm tra kiểu thời gian biên dịch (đó là một điều đủ tốt) nhưng không tính toán thời gian biên dịch (đó là những gì bạn đang làm ví dụ). – MatiasFG

+0

@MatiasFG - chính xác điểm - generics được tự động biên soạn nhưng loại kiểm tra cho mã của họ được thực hiện tại thời gian biên dịch của loại chung chung. Tôi nhớ là không có chức năng template trong C# khi bắt đầu làm mẫu Meta-Programming trong C++ ngay trước khi trở thành một nhà phát triển C# toàn thời gian - tuy nhiên Generics rất rất hay và họ rất vui vì chúng tôi có chúng! –

3

Hãy xem bài viết này về sự khác biệt giữa C# generics và C++ templates:

Tôi nghĩ rằng ví dụ của bạn được bao gồm ở đó.

MSDN Link

+0

Cảm ơn, nó loại trừ trường hợp này: "C# không cho phép các tham số mẫu không kiểu, chẳng hạn như mẫu C {}.". Là tái cấu trúc cho một hàm, như tôi đã làm trong câu hỏi trên, cách tốt nhất để đi về việc chuyển các kiểu mẫu này là gì? –

+1

Trong trường hợp trên, việc tái cấu trúc một phương thức có lẽ là điều tốt nhất để làm. Nhưng mỗi tình huống cần phải được xử lý trên cơ sở từng trường hợp. Đôi khi bạn có thể muốn/cần cung cấp một giá trị cho hàm tạo của lớp. –

+1

Điều tốt là C# không yêu cầu hằng số thời gian biên dịch như C++ - ví dụ khi khai báo một mảng--, vì vậy một phép tính thời gian chạy trong hầu hết các trường hợp sẽ mang lại kết quả mong đợi. –

1

Câu trả lời ngắn gọn là, không phải tất cả những gì có thể được thực hiện trong C++ mẫu có thể được thực hiện trong C# generics. Trong trường hợp các mẫu chấp nhận các giá trị không phải kiểu, mỗi tình huống sẽ phải được xử lý và tính toán lại một cách thích hợp trên cơ sở từng trường hợp.

0

Đây là càng gần như tôi có thể nghĩ:

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

Vấn đề là bạn không thể xác định Generics và hạn chế nó để ValueTypes. Điều này sẽ hoạt động cho byte, Int16 và Int32. Ngoài ra đối với các giá trị nhỏ của Int64.

+0

Để làm rõ, vấn đề là bạn không thể giới hạn các giá trị số. Bạn có thể đặt một số ràng buộc trên đó cho struct và IComparable, nhưng đó là khó đủ để xác định số/không phải là số. –

5

Trong ví dụ bên dưới… mẫu có vẻ chấp nhận giá trị chứ không phải loại.

Đây không phải là vấn đề lớn nhất của bạn. Trong thực tế, điều này về mặt lý thuyết có thể được giải quyết trong C# bằng cách sử dụng một biểu thức Church numeral hoặc Peano dựa vào các kiểu chung chung lồng nhau.

Tuy nhiên, vấn đề của bạn là C# không cho phép mẫu chuyên môn. Chuyên môn về mẫu chịu trách nhiệm trong ví dụ của bạn để xác định giai thừa của 01, thay vì giống như tất cả các số khác. C# không cho phép thực hiện điều đó.

Vì vậy, không có cách nào để xác định trường hợp cơ bản trong định nghĩa mẫu đệ quy (chung) và do đó không có đệ quy. C# generics không Turing hoàn chỉnh, trong khi các mẫu C++ là.


Something như thế này:

class Zero { } 

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

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

Thực hiện các hoạt động trên những con số này là trái như một bài tập cho người đọc.