2013-03-20 36 views
10

Khi gọi một phương thức chung để lưu trữ một đối tượng, đôi khi cần xử lý một loại cụ thể khác nhau. Tôi biết rằng bạn không thể quá tải dựa trên những ràng buộc, nhưng bất kỳ sự thay thế nào khác dường như đều có những vấn đề riêng.Quá tải các phương pháp chung

public bool Save<T>(T entity) where T : class 
{ ... some storage logic ... } 

Những gì tôi muốn làm là một cái gì đó như sau:

public bool Save<SpecificClass>(T entity) 
{ ... special logic ... } 

Trong quá khứ nhóm chúng tôi đã tạo ra 'một lần' phương pháp để tiết kiệm các lớp này như sau:

public bool SaveSpecificClass(SpecificClass sc) 
{ ... special logic ... } 

Tuy nhiên, nếu bạn không BIẾT chức năng đó tồn tại, và bạn cố gắng sử dụng chung (Lưu) thì bạn có thể gặp phải một loạt các sự cố mà 'một lần' được cho là sửa chữa. Điều này có thể tồi tệ hơn nếu một nhà phát triển mới đến cùng, thấy vấn đề với chung, và quyết định anh ta sẽ sửa chữa nó với chức năng một lần của riêng mình.

Vì vậy, ...

Các tùy chọn để giải quyết vấn đề dường như phổ biến này là gì?

Tôi đã xem xét và sử dụng UnitOfWork và ngay bây giờ dường như là tùy chọn chỉ thực sự giải quyết vấn đề - nhưng có vẻ như tấn công một con ruồi bằng búa tạ.

+2

Không giống như C++, C# không cho phép chuyên môn mẫu –

+0

Các phương thức Save() này có được đặt trong một số lớp trợ giúp nhẹ hoặc trong một số lớp thực thể không? Tôi chỉ nghĩ về thừa kế nhưng điều quan trọng là phải chắc chắn đây là cách đúng đắn bởi vì thừa kế không phải lúc nào cũng được sử dụng đúng cách – sll

+0

Có thể trùng lặp [Làm thế nào để làm chuyên môn hóa mẫu trong C#] (http://stackoverflow.com/questions/600978/how -to-do-template-specialization-in-c-sharp) –

Trả lời

12

Bạn có thể làm:

public bool Save<T>(T entity) where T : class 
{ ... some storage logic ... } 

public bool Save(SpecificClass entity) 
{ ... special logic ... } 

Ví dụ:

public class SpecificClass 
{ 
} 

public class Specializer 
{ 
    public bool GenericCalled; 
    public bool SpecializedCalled; 

    public bool Save<T>(T entity) where T : class 
    { 
     GenericCalled = true; 
     return true; 
    } 

    public bool Save(SpecificClass entity) 
    { 
     SpecializedCalled = true; 
     return true; 
    } 
} 

public class Tests 
{ 
    [Test] 
    public void TestSpecialization() 
    { 
     var x = new Specializer(); 
     x.Save(new SpecificClass()); 
     Assert.IsTrue(x.SpecializedCalled); 
     Assert.IsFalse(x.GenericCalled); 
    } 
} 
+0

Điều này làm việc, tôi không thể tin rằng tôi đã không nghĩ về điều này đơn giản của một giải pháp ... –

+0

Tôi không thể làm cho nó hoạt động: phiên bản của hàm được gọi trong mọi trường hợp. – MarkusParker

+0

@MarkusParker Bạn có thể cung cấp một ví dụ vì điều này sẽ hoạt động tốt, như được chi tiết bởi bản cập nhật của tôi cho thấy nó hoạt động trong một bài kiểm tra đơn vị. Hãy nhớ rằng biến được sử dụng để vượt qua 'SpecificClass' phải thuộc kiểu' SpecificClass' trừ khi nó là một lớp 'dynamic' khi C# thực hiện đa hình thời gian biên dịch. –

0

Tại sao sử dụng tên gọi khác nhau cho phương pháp của bạn?

Xem sau:

public class Entity 
    { 
    } 

    public class SpecificEntity : Entity 
    { 
    } 

    public class Program 
    { 
     public static void Save<T>(T entity) 
      where T : class 
     { 
      Console.WriteLine(entity.GetType().FullName); 
     } 

     public static void Save(SpecificEntity entity) 
     { 
      Console.WriteLine(entity.GetType().FullName); 
     } 

     private static void Main(string[] args) 
     { 
      Save(new Entity());   // ConsoleApplication13.Entity 
      Save(new SpecificEntity()); // ConsoleApplication13.SpecificEntity 

      Console.ReadKey(); 
     } 
    } 
2

Vâng, về cơ bản C# không cho phép mẫu chuyên môn, ngoại trừ thông qua thừa kế như thế này:

interface IFoo<T> { } 
class Bar { } 

class FooBar : IFoo<Bar> { } 

Ít nhất là nó không hỗ trợ này trong suốt thời gian biên dịch. Tuy nhiên bạn có thể sử dụng RTTI để làm những gì bạn đang cố gắng để đạt được:

public bool Save<T>(T entity) 
{ 
    // Check if "entity" is of type "SpecificClass" 
    if (entity is SpecificClass) 
    { 
     // Entity can be safely casted to "SpecificClass" 
     return SaveSpecificClass((SpecificClass)entity); 
    } 

    // ... other cases ... 
} 

Các is expression là khá tiện dụng để làm kiểm tra kiểu thời gian chạy. Nó hoạt động tương tự như đoạn mã sau:

if (entity.GetType() == typeof(SpecificClass)) 
    // ... 

EDIT: Nó là khá phổ biến với nhiều loại chưa biết sử dụng các mô hình sau:

if (entity is Foo) 
    return DoSomethingWithFoo((Foo)entity); 
else if (entity is Bar) 
    return DoSomethingWithBar((Bar)entity); 
else 
    throw new NotSupportedException(
     String.Format("\"{0}\" is not a supported type for this method.", entity.GetType())); 

EDIT 2: Là câu trả lời khác đề nghị quá tải phương pháp với SpecializedClass bạn cần phải cẩn thận nếu bạn đang làm việc với đa hình.Nếu bạn đang sử dụng giao diện cho kho lưu trữ của bạn (đây thực sự là một cách tốt để thiết kế mẫu kho lưu trữ), có trường hợp quá tải sẽ dẫn đến trường hợp bạn gọi phương thức sai, bất kể bạn đang truyền đối tượng SpecializedClass để giao diện:

interface IRepository 
{ 
    bool Save<T>(T entity) 
     where T : class; 
} 

class FooRepository : IRepository 
{ 
    bool Save<T>(T entity) 
    { 
    } 

    bool Save(Foo entity) 
    { 
    } 
} 

này hoạt động nếu bạn trực tiếp gọi FooRepository.Save với một thể hiện của Foo:

var repository = new FooRepository(); 
repository.Save(new Foo()); 

Nhưng điều này không có tác dụng nếu bạn đang gọi giao diện (ví dụ: nếu bạn đang sử dụng mô hình để triển khai tạo kho lưu trữ):

IRepository repository = GetRepository<FooRepository>(); 
repository.Save(new Foo()); // Attention! Call's FooRepository.Save<Foo>(Foo entity) instead of FooRepository.Save(Foo entity)! 

Sử dụng RTTI chỉ có một phương pháp Save và bạn sẽ ổn.

+0

Điều gì xảy ra với quá tải mất một kiểu cụ thể như' Save (SpecificClass entity) ' –

+0

Không có gì sai với nó, nhưng nó vẫn là quá tải, không phải là chuyên môn. ;) – Carsten

+0

Câu hỏi * là * về "quá tải" ... :) –

3

Bởi vì chức năng và điều hành quá tải liên quan đến Generics đang bị ràng buộc tại thời gian biên dịch chứ không phải chạy theo thời gian, nếu mã có hai phương pháp:

public bool Save<T>(T entity) ... 
public bool Save(SomeClass entity) ... 

sau đó mã mà cố gắng gọi Save(Foo) nơi Foo là một biến của một số loại chung sẽ luôn gọi quá tải cũ, ngay cả khi loại chung xảy ra là SomeClass. Đề nghị của tôi để giải quyết đó sẽ là để xác định một giao diện chung ISaver<in T> với một phương pháp không chung chung DoSave(T param). Có lớp cung cấp phương thức Save triển khai tất cả các giao diện chung thích hợp cho các kiểu mà nó có thể xử lý. Sau đó, có phương pháp Save<T> của đối tượng cố gắng truyền this đến ISaver<T>. Nếu dàn diễn viên thành công, sử dụng kết quả ISaver<T>; nếu không thực hiện một lưu chung chung. Với điều kiện là khai báo kiểu lớp liệt kê tất cả các giao diện thích hợp cho các kiểu nó có thể lưu, cách tiếp cận này sẽ gửi các cuộc gọi đến các phương thức thích hợp Save.

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