2009-03-02 42 views
66

Bạn sẽ chuyên môn hóa như thế nào trong C#? Tôi sẽ gây ra sự cố. Bạn có một loại mẫu, bạn không có ý tưởng nó là gì. Nhưng bạn biết nếu nó có nguồn gốc từ XYZ bạn muốn gọi .alternativeFunc(). Một cách tuyệt vời là gọi một hàm hoặc lớp chuyên biệt và có trả về normalCall .normalFunc() trong khi có chuyên môn khác về bất kỳ kiểu dẫn xuất XYZ nào để gọi .alternativeFunc(). Làm thế nào điều này sẽ được thực hiện trong C#?Cách thực hiện chuyên môn mẫu trong C#

+1

Tôi không hiểu câu hỏi. Đây có phải là thừa kế không bình thường không? –

Trả lời

53

giả sử bạn đang nói về chuyên môn mẫu vì có thể thực hiện với mẫu C++ - một tính năng như thế này không thực sự khả dụng trong C#. Điều này là do các generic của C# không được xử lý trong quá trình biên dịch và có nhiều tính năng hơn trong thời gian chạy.

Tuy nhiên, bạn có thể đạt được hiệu ứng tương tự bằng cách sử dụng các phương pháp mở rộng C# 3.0. Dưới đây là ví dụ cho thấy cách thêm phương thức tiện ích mở rộng chỉ cho loại "MyClass", giống như chuyên môn mẫu. Tuy nhiên lưu ý rằng bạn không thể sử dụng để che giấu thực hiện mặc định của phương pháp này, bởi vì biên dịch C# luôn thích các phương pháp tiêu chuẩn để phương pháp khuyến nông:

class MyClass<T> { 
    public int Foo { get { return 10; } } 
} 
static class MyClassSpecialization { 
    public static void Bar(this MyClass<int> cls) { 
    return cls.Foo + 20; 
    } 
} 

Bây giờ bạn có thể viết này:

var cls = new MyClass<int>(); 
cls.Bar(); 

Nếu bạn muốn có một trường hợp mặc định cho phương thức sẽ được sử dụng khi không có chuyên môn nào được cung cấp, hơn tôi tin rằng viết một phương thức mở rộng "Bar" chung cần thực hiện thủ thuật:

public static void Bar<T>(this MyClass<T> cls) { 
    return cls.Foo + 42; 
    } 
+0

Thuộc tính Foo vs Phương thức thanh ... không thực sự có vẻ giống như một chuyên môn điển hình ... –

+0

Đó là một giải pháp tuyệt vời, +1 –

+1

Không, nó không phải là đặc trưng điển hình, nhưng đó là điều dễ dàng duy nhất mà bạn có thể làm ... (AFAIK) –

0

Nếu bạn chỉ muốn kiểm tra xem một loại được derrived từ XYZ, sau đó bạn có thể sử dụng:

theunknownobject.GetType().IsAssignableFrom(typeof(XYZ)); 

Nếu vậy, bạn có thể cast "theunknownobject" để XYZ và gọi alternativeFunc() như thế này:

XYZ xyzObject = (XYZ)theunknownobject; 
xyzObject.alternativeFunc(); 

Hy vọng điều này sẽ hữu ích.

+1

Tôi không biết nhiều C#, nhưng bất cứ ai đã bỏ phiếu cho bạn đều nên biết lý do tại sao. Tôi không có ý tưởng whats sai khi câu trả lời của bạn hoặc nếu bất cứ điều gì là sai với nó. –

+0

Bạn cũng không chắc chắn. Nó có vẻ hợp lệ với tôi. Mặc dù tiết kiệm hơn một chút so với cần thiết. – jalf

+2

Đó không phải là tôi mà là bởi vì câu trả lời hoàn toàn không liên quan đến câu hỏi. Tra cứu '" C++ mẫu chuyên môn "' – georgiosd

74

Trong C#, gần nhất với chuyên môn hóa là sử dụng quá tải cụ thể hơn; Tuy nhiên, điều này là dễ vỡ, và không bao gồm mọi cách sử dụng có thể. Ví dụ:

void Foo<T>(T value) {Console.WriteLine("General method");} 
void Foo(Bar value) {Console.WriteLine("Specialized method");} 

Ở đây, nếu trình biên dịch biết các loại lúc biên dịch, nó sẽ chọn cụ thể nhất:

Bar bar = new Bar(); 
Foo(bar); // uses the specialized method 

Tuy nhiên ....

void Test<TSomething>(TSomething value) { 
    Foo(value); 
} 

sẽ sử dụng Foo<T> ngay cả đối với TSomething=Bar, vì điều này được đốt cháy trong thời gian biên dịch.

Một cách tiếp cận khác là sử dụng thử nghiệm loại trong phạm vi một phương pháp chung - tuy nhiên, đây thường là ý tưởng tồi và không được khuyến nghị.

Về cơ bản, C# chỉ không muốn bạn làm việc với các chuyên ngành, trừ đa hình:

class SomeBase { public virtual void Foo() {...}} 
class Bar : SomeBase { public override void Foo() {...}} 

đây Bar.Foo sẽ luôn giải quyết để ghi đè lên chính xác.

13

Bằng cách thêm lớp trung gian và từ điển, có thể chuyên môn hóa.

Để chuyên về T, chúng tôi tạo giao diện chung, có phương pháp được gọi là (ví dụ: Áp dụng. Đối với các lớp cụ thể mà giao diện được thực hiện, xác định phương thức Áp dụng cụ thể cho lớp đó. Lớp trung gian này được gọi là lớp đặc điểm.

Lớp đặc điểm đó có thể được chỉ định làm tham số trong lệnh gọi của phương thức chung, mà sau đó (tất nhiên) luôn thực hiện đúng.

Thay vì chỉ định theo cách thủ công, lớp đặc điểm cũng có thể được lưu trữ trong toàn cầu IDictionary<System.Type, object>. Sau đó nó có thể được nhìn lên và thì đấy, bạn có chuyên môn thực sự ở đó.

Nếu thuận tiện, bạn có thể hiển thị nó trong một phương pháp mở rộng.

class MyClass<T> 
{ 
    public string Foo() { return "MyClass"; } 
} 

interface BaseTraits<T> 
{ 
    string Apply(T cls); 
} 

class IntTraits : BaseTraits<MyClass<int>> 
{ 
    public string Apply(MyClass<int> cls) 
    { 
     return cls.Foo() + " i"; 
    } 
} 

class DoubleTraits : BaseTraits<MyClass<double>> 
{ 
    public string Apply(MyClass<double> cls) 
    { 
     return cls.Foo() + " d"; 
    } 
} 

// Somewhere in a (static) class: 
public static IDictionary<Type, object> register; 
register = new Dictionary<Type, object>(); 
register[typeof(MyClass<int>)] = new IntTraits(); 
register[typeof(MyClass<double>)] = new DoubleTraits(); 

public static string Bar<T>(this T obj) 
{ 
    BaseTraits<T> traits = register[typeof(T)] as BaseTraits<T>; 
    return traits.Apply(obj); 
} 

var cls1 = new MyClass<int>(); 
var cls2 = new MyClass<double>(); 

string id = cls1.Bar(); 
string dd = cls2.Bar(); 

Xem này link vào blog gần đây của tôi và các mô tả mở rộng.

+0

Đây là Mẫu Nhà máy và đó là một cách phù hợp để đối phó với * một số * của những thiếu sót của generics – Yaur

+1

@Yaur Tôi trông giống như một mẫu Decorator sách giáo khoa với tôi. –

5

Một số câu trả lời được đề xuất đang sử dụng thông tin loại thời gian chạy: vốn đã chậm hơn các lần gọi phương thức liên kết biên dịch.

Trình biên dịch không thực thi chuyên môn cũng như trong C++.

Tôi khuyên bạn nên xem xét PostSharp để biết cách chèn mã sau khi trình biên dịch thông thường được thực hiện để đạt được hiệu ứng tương tự như C++.

2

Tôi cũng đang tìm kiếm một mẫu để mô phỏng chuyên môn về mẫu. Có một số cách tiếp cận có thể hoạt động trong một số trường hợp. Tuy nhiên, về trường hợp này,

static void Add<T>(T value1, T value2) 
{ 
    //add the 2 numeric values 
} 

Có thể chọn hành động sử dụng câu lệnh, ví dụ: if (typeof(T) == typeof(int)). Nhưng có một cách tốt hơn để mô phỏng mẫu chuyên môn thực sự với chi phí của một hàm ảo cuộc gọi duy nhất:

public interface IMath<T> 
{ 
    T Add(T value1, T value2); 
} 

public class Math<T> : IMath<T> 
{ 
    public static readonly IMath<T> P = Math.P as IMath<T> ?? new Math<T>(); 

    //default implementation 
    T IMath<T>.Add(T value1, T value2) 
    { 
     throw new NotSupportedException();  
    } 
} 

class Math : IMath<int>, IMath<double> 
{ 
    public static Math P = new Math(); 

    //specialized for int 
    int IMath<int>.Add(int value1, int value2) 
    { 
     return value1 + value2; 
    } 

    //specialized for double 
    double IMath<double>.Add(double value1, double value2) 
    { 
     return value1 + value2; 
    } 
} 

Bây giờ chúng ta có thể viết, mà không cần phải biết loại trước:

static T Add<T>(T value1, T value2) 
{ 
    return Math<T>.P.Add(value1, value2); 
} 

private static void Main(string[] args) 
{ 
    var result1 = Add(1, 2); 
    var result2 = Add(1.5, 2.5); 

    return; 
} 

Nếu chuyên môn hóa không chỉ nên được gọi cho các loại được triển khai, mà còn có các kiểu có nguồn gốc, có thể sử dụng tham số In cho giao diện. Tuy nhiên, trong trường hợp này, các kiểu trả về của các phương thức không thể là kiểu generic T nữa.

0

Tôi nghĩ rằng có một cách để đạt được nó với NET 4+ sử dụng độ phân giải năng động:

static class Converter<T> 
{ 
    public static string Convert(T data) 
    { 
     return Convert((dynamic)data); 
    } 

    private static string Convert(Int16 data) => $"Int16 {data}"; 
    private static string Convert(UInt16 data) => $"UInt16 {data}"; 
    private static string Convert(Int32 data) => $"Int32 {data}"; 
    private static string Convert(UInt32 data) => $"UInt32 {data}"; 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     Console.WriteLine(Converter<Int16>.Convert(-1)); 
     Console.WriteLine(Converter<UInt16>.Convert(1)); 
     Console.WriteLine(Converter<Int32>.Convert(-1)); 
     Console.WriteLine(Converter<UInt32>.Convert(1)); 
    } 
} 

Output:

Int16 -1 
UInt16 1 
Int32 -1 
UInt32 1 

nào cho thấy một thực hiện khác nhau được gọi với nhiều loại khác nhau.

Các vấn đề liên quan