2009-07-16 27 views
7

Một hàm trừu tượng phải được thực hiện bởi tất cả các lớp cụ thể.Làm thế nào tôi có thể buộc tất cả các lớp dẫn xuất để thực hiện một phương thức trừu tượng hoặc thuộc tính?

Đôi khi bạn muốn ép buộc tất cả các lớp phái sinh để thực hiện hàm trừu tượng, thậm chí dẫn xuất của các lớp cụ thể.

class Base { protected abstract Base Clone(); } 
class Concrete : Base { protected override Base Clone(){...}; } 
class Custom : Concrete {} 

Tôi muốn trình biên dịch để nói với các lập trình viên rằng lớp Custom cần phải thực hiện Clone(). Có cách nào không?

+0

Chừng một số lớp bê tông trong cây thừa kế đã thực hiện phương pháp trừu tượng, tại sao bạn sẽ quan tâm nếu một đứa trẻ của lớp bê tông đó dựa vào việc thực hiện của cha mẹ? –

+0

Tôi đến muộn nhưng tôi có cùng một vấn đề.Tôi muốn có một vòng loại 'abstract trừu tượng' để buộc mọi lớp dẫn xuất phải thực hiện hàm nhưng ngăn chặn mã của classe hoàn toàn rơi trở lại việc thực thi lớp cha. 'Clone()' là một ví dụ tốt. Trường hợp của tôi giống như 'ToString()'. Một cái gì đó chung chung nhưng khác biệt cho mỗi lớp. –

+0

Thực ra điều này phải xảy ra thường xuyên hơn (hoặc tôi thiếu một cái gì đó). –

Trả lời

12

Trình biên dịch không thể thực thi điều này. Bạn có thể xem xét cách viết plugin phân tích của riêng mình tới Gendarme hoặc FxCop để thực thi các yêu cầu đó.

+0

Plugin sẽ phân biệt như thế nào giữa trường hợp bạn muốn ép buộc yêu cầu trên tất cả các dẫn xuất hoặc chỉ là các dẫn xuất ngay lập tức? –

+0

Thuộc tính sẽ là cách tiếp cận đơn giản nhất - [MustImplement] hoặc tương tự. –

2

Bạn sẽ phải thực hiện Concrete một lớp trừu tượng để thực thi điều đó.

+0

Mục tiêu là có một lớp cơ sở dễ mở rộng. Bạn không có quyền kiểm soát cách mọi người sẽ mở rộng lớp học của bạn. Bạn thậm chí có thể không ở quanh. Giải nhất là ghi lại yêu cầu trong mã mà trình biên dịch có thể thực thi. –

0

Bạn có thể kiểm tra điều này tại thời gian chạy bằng cách sử dụng phản chiếu và ném một ngoại lệ để phá vỡ việc thực hiện, phá hoại sự tàn phá đối với người dùng "bất lịch sự" trong thư viện của bạn. Hiệu suất khôn ngoan không phải là rất khôn ngoan, mặc dù bạn có thể lưu trữ một static HashSet <System.Type> trong lớp trừu tượng cơ sở với tất cả các loại được xác minh.

Tôi nghĩ rằng đặt cược tốt nhất của bạn là cung cấp tài liệu rõ ràng cho mọi người dùng mã của bạn thấy rằng cần thiết để ghi đè phương thức Clone().

3

Tôi đoán bạn không thực sự cần TẤT CẢ các lớp dẫn xuất để triển khai phương pháp trừu tượng, nhưng chắc chắn có vẻ như bạn có một chút mùi mã trong thiết kế của mình.

Nếu bạn không có bất kỳ chức năng nào trong phương thức Concrete.Clone(), thì bạn có thể làm cho lớp 'Concrete' của bạn trừu tượng (chỉ cần chắc chắn thay đổi tên ;-). Bỏ qua bất kỳ tham chiếu nào của phương thức Clone().

abstract class Base { protected abstract void Clone(); } 
abstract class Concrete : Base { } 
class Custom : Concrete { protected override void Clone() { /* do something */ } } 

Nếu bạn có một số chức năng cơ bản trong phương pháp Concrete.Clone(), nhưng cần thông tin chi tiết từ một mức độ cao hơn, sau đó phá vỡ nó ra thành phương pháp trừu tượng riêng của nó hoặc tài sản buộc một thực hiện mức độ cao hơn để cung cấp này thông tin.

abstract class Base { protected abstract void Clone(); } 

abstract class ConcreteForDatabases : Base 
{ 
    protected abstract string CopyInsertStatemement {get;} 

    protected override void Clone() 
    { 
     // setup db connection & command objects 
     string sql = CopyInsertStatemement; 
     // process the statement 
     // clean up db objects 
    } 
} 

class CustomBusinessThingy : ConcreteForDatabases 
{ 
    protected override string CopyInsertStatemement {get{return "insert myTable(...) select ... from myTable where ...";}} 
} 
+0

Tôi đã sửa phương thức Clone mẫu của mình để trả về Base. Khi bạn thực hiện đối tượng nhân bản bằng cách sử dụng các phương thức Clone ảo, bạn cần * tất cả * các lớp phái sinh để thực hiện phương thức trừu tượng. –

-2

remove thực hiện trong concreate lớp hoặc sử dụng cơ sở lớp

0

tôi đã thực hiện các bài kiểm tra NUnit sau đó sử dụng phản xạ để kiểm tra việc thực hiện. Hy vọng rằng bạn có thể thích ứng khi cần thiết.

Tôi nghi ngờ nó sẽ không xử lý các phương pháp quá tải tốt, nhưng nó đủ cho những gì tôi muốn.

(Cảm nhận hoan nghênh)

/// <summary> 
/// Use on a (possibly abstract) method or property to indicate that all subclasses must provide their own implementation. 
/// 
/// This is stronger than just abstract, as when you have 
/// 
/// A { public abstract void Method()} 
/// B: A { public override void Method(){} } 
/// C: B {} 
/// 
/// C will be marked as an error 
/// </summary> 
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Method)] 
public class AllSubclassesMustOverrideAttribute : Attribute 
{ 

} 

[TestFixture] 
public class AllSubclassesMustOverrideAttributeTest 
{ 
    [Test] 
    public void SubclassesOverride() 
    { 
     var failingClasses = new List<string>(); 

     foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      try 
      { 
       foreach (var type in assembly.GetTypes()) 
       { 
        foreach (var methodInfo in type.GetMethods().Where(m => m.HasAttributeOfType<AllSubclassesMustOverrideAttribute>())) 
        { 
         foreach (var subClass in type.ThisTypeAndSubClasses()) 
         { 
          var subclassMethod = subClass.GetMethod(methodInfo.Name); 

          if (subclassMethod.DeclaringType != subClass) 
          { 
           failingClasses.Add(string.Format("Class {0} has no override for method {1}", subClass.FullName, methodInfo.Name)); 
          } 
         } 
        } 

        foreach (var propertyInfo in type.GetProperties().Where(p => p.HasAttributeOfType<AllSubclassesMustOverrideAttribute>())) 
        { 
         foreach (var subClass in type.ThisTypeAndSubClasses()) 
         { 
          var subclassProperty = subClass.GetProperty(propertyInfo.Name); 

          if (subclassProperty.DeclaringType != subClass) 
          { 
           failingClasses.Add(string.Format("Class {0} has no override for property {1}", subClass.FullName, propertyInfo.Name)); 
          } 
         } 

        } 
       } 
      } 
      catch (ReflectionTypeLoadException) 
      { 
       // This will happen sometimes when running the tests in the NUnit runner. Ignore. 
      } 
     } 

     if (failingClasses.Any()) 
     { 
      Assert.Fail(string.Join("\n", failingClasses)); 
     } 
    } 
} 

Nó sử dụng các phương pháp khuyến nông sau

public static bool HasAttributeOfType<T>(this ICustomAttributeProvider provider) 
    { 
     return provider.GetCustomAttributes(typeof(T), false).Length > 0; 
    } 

    public static IEnumerable<Type> ThisTypeAndSubClasses(this Type startingType) 
    { 
     var types = new List<Type>(); 
     foreach (var assembly in AppDomain.CurrentDomain.GetAssemblies()) 
     { 
      try 
      { 
       foreach (var type in assembly.GetTypes()) 
       { 
        if (startingType.IsAssignableFrom(type)) 
        { 
         types.Add(type); 
        } 
       } 
      } 
      catch (ReflectionTypeLoadException) 
      { 
       // Some assembly types are unable to be loaded when running as nunit tests. 
       // Move on to the next assembly 
      } 
     } 
     return types; 
    } 
Các vấn đề liên quan