2009-04-17 49 views
12

Tôi có một lớp được sử dụng trong ứng dụng máy khách và trong ứng dụng máy chủ. Trong ứng dụng máy chủ, tôi thêm một số chức năng vào các phương thức mở rộng máng lớp. Hoạt động tuyệt vời. Bây giờ tôi muốn nhiều hơn một chút:Phương pháp mở rộng ảo?

Lớp học của tôi (B) kế thừa từ một lớp khác (A).

Tôi muốn đính kèm hàm ảo vào A (giả sử Execute()), sau đó triển khai hàm đó trong B. Nhưng chỉ trong máy chủ. Phương thức Execute() sẽ cần phải làm những thứ chỉ có thể thực hiện trên máy chủ, sử dụng các kiểu mà chỉ máy chủ biết.

Có nhiều loại kế thừa từ A giống như B, và tôi muốn triển khai Thực hiện() cho mỗi người trong số họ.

Tôi đã hy vọng tôi có thể thêm phương thức mở rộng ảo vào A, nhưng ý tưởng đó dường như không bay. Tôi đang tìm cách thanh lịch nhất để giải quyết vấn đề này, có hoặc không có phương pháp mở rộng.

Trả lời

4

Không, không có những thứ như phương pháp mở rộng ảo. Bạn có thể sử dụng quá tải, nhưng điều đó không hỗ trợ đa hình. Có vẻ như bạn có thể muốn nhìn vào một cái gì đó giống như dependency injection (vv) để có mã khác nhau (phụ thuộc) được thêm vào trong các môi trường khác nhau - và sử dụng nó trong các phương pháp ảo thường xuyên:

class B { 
    public B(ISomeUtility util) { 
     // store util 
    } 
    public override void Execute() { 
     if(util != null) util.Foo(); 
    } 
} 

Sau đó sử dụng một khuôn khổ DI để cung cấp triển khai ISomeUtility dành riêng cho máy chủ cho B khi chạy. Bạn có thể làm điều tương tự với một trung tâm static registry (IOC, nhưng không DI):

override void Execute() { 
     ISomeUtility util = Registry.Get<ISomeUtility>(); 
     if(util != null) util.Foo(); 
    } 

(nơi bạn sẽ cần phải viết Registry vv; cộng trên máy chủ, đăng ký thực hiện ISomeUtility)

+0

Nhờ Marc. Tôi sẽ thực hiện một cái gì đó như thế này. Đó là một chút khó khăn hơn cho tôi, khi tôi sắp xếp các lớp này, và gửi chúng qua dây từ máy chủ đến máy khách và ngược lại. Vì vậy, DI truyền thống có thể hơi phức tạp, nhưng tôi đoán tôi có thể thực hiện một máy chủ tương đương với lớp B, (có thể kế thừa từ B), và khi máy khách gửi máy chủ một thể hiện của B, tôi sẽ phải thay thế nó bằng một phiên bản mới của ServerB. – Lucas

+0

Cách tiếp cận đăng ký hoạt động OK với tuần tự hóa. Tôi sử dụng phương pháp đó để chia sẻ lắp ráp với các đối tượng WCF ... –

0

Ảo ngụ ý thừa kế theo cách OOP và các phương thức mở rộng là "chỉ" các phương thức tĩnh thông qua một đường cú pháp, trình biên dịch cho phép bạn giả vờ gọi một thể hiện kiểu tham số đầu tiên của nó. Vì vậy, không, phương pháp mở rộng ảo nằm ngoài câu hỏi.

Kiểm tra câu trả lời của Marc Gravell để biết giải pháp có thể cho vấn đề của bạn.

0

Bạn có thể triển khai thanh ghi dịch vụ. Ví dụ (phía máy chủ):

static IDictionary<Type, IService> serviceRegister; 

public void ServerMethod(IBusinessType object) 
{ 
    serviceRegister[obect.GetType()].Execute(object); 
} 

Những gì bạn cần là các dịch vụ thay thế trong máy chủ, thực hiện chức năng phía máy chủ thay vì phương pháp mở rộng. Tôi sẽ không đặt nhiều logic vào các phương pháp mở rộng.

0

Hãy để tôi kiểm tra: bạn có một hệ thống phân cấp lớp kế thừa từ A, có lẽ được cấu trúc theo miền doanh nghiệp của bạn. Sau đó, bạn muốn thêm các hành vi tùy thuộc vào nơi các lớp thực thi. Cho đến nay bạn đã sử dụng các phương thức mở rộng, nhưng bây giờ bạn thấy bạn không thể làm cho chúng thay đổi theo thứ bậc lớp của bạn. Bạn đang gắn các loại hành vi nào vào máy chủ?

Nếu đó là những thứ như quản lý giao dịch và bảo mật, các chính sách được thực hiện thông qua tiêm phụ thuộc à la đề xuất của Marc nên hoạt động tốt. Bạn cũng có thể xem xét việc triển khai Strategy pattern thông qua các đại biểu và lambdas, để có phiên bản DI hạn chế hơn. Tuy nhiên, những gì không rõ ràng là làm thế nào mã khách hàng hiện đang sử dụng các lớp học của bạn và các phương pháp mở rộng của họ trên máy chủ. Các lớp khác phụ thuộc vào cách bạn thêm chức năng phía máy chủ như thế nào?Có phải họ là các lớp chỉ phía máy chủ hiện đang mong đợi để tìm các phương pháp mở rộng?

Trong mọi trường hợp, có vẻ như bạn sẽ cần một thiết kế thử nghiệm cẩn thận và chiến lược thử nghiệm vì bạn đang giới thiệu biến thể dọc theo hai chiều đồng thời (phân cấp thừa kế, môi trường thực thi). Bạn đang sử dụng thử nghiệm đơn vị, tôi tin tưởng? Kiểm tra xem bạn có chọn giải pháp nào không (ví dụ: DI thông qua cấu hình) tương tác tốt với thử nghiệm và chế nhạo.

2

Tôi sẽ đề xuất điều gì đó như sau. Mã này có thể được cải thiện bằng cách thêm hỗ trợ phát hiện các kiểu phân cấp lớp trung gian không có ánh xạ dispatch và gọi phương thức dispatch gần nhất dựa trên hệ thống phân cấp thời gian chạy. Nó cũng có thể được cải thiện bằng cách sử dụng sự phản chiếu để phát hiện tình trạng quá tải của ExecuteInteral() và tự động thêm chúng vào bản đồ công văn.

using System; 
using System.Collections.Generic; 

namespace LanguageTests2 
{ 
    public class A { } 

    public class B : A {} 

    public class C : B {} 

    public static class VirtualExtensionMethods 
    { 
     private static readonly IDictionary<Type,Action<A>> _dispatchMap 
      = new Dictionary<Type, Action<A>>(); 

     static VirtualExtensionMethods() 
     { 
      _dispatchMap[typeof(A)] = x => ExecuteInternal((A)x); 
      _dispatchMap[typeof(B)] = x => ExecuteInternal((B)x); 
      _dispatchMap[typeof(C)] = x => ExecuteInternal((C)x); 
     } 

     public static void Execute(this A instance) 
     { 
      _dispatchMap[instance.GetType()](instance); 
     } 

     private static void ExecuteInternal(A instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 

     private static void ExecuteInternal(B instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 

     private static void ExecuteInternal(C instance) 
     { 
      Console.WriteLine("\nCalled ToString() on: " + instance); 
     } 
    } 

    public class VirtualExtensionsTest 
    { 
     public static void Main() 
     { 
      var instanceA = new A(); 
      var instanceB = new B(); 
      var instanceC = new C(); 

      instanceA.Execute(); 
      instanceB.Execute(); 
      instanceC.Execute(); 
     } 
    } 
} 
3

Bạn có thể sử dụng chức năng kiểu mới năng động để tránh việc phải xây dựng một registry của các loại phương pháp:

using System; 
using System.Collections.Generic; 
using System.Linq; 
using visitor.Extension; 

namespace visitor 
{ 
    namespace Extension 
    { 
     static class Extension 
     { 
      public static void RunVisitor(this IThing thing, IThingOperation thingOperation) 
      { 
       thingOperation.Visit((dynamic)thing); 
      } 

      public static ITransformedThing GetTransformedThing(this IThing thing, int arg) 
      { 
       var x = new GetTransformedThing {Arg = arg}; 
       thing.RunVisitor(x); 
       return x.Result; 
      } 
     } 
    } 

    interface IThingOperation 
    { 
     void Visit(IThing iThing); 
     void Visit(AThing aThing); 
     void Visit(BThing bThing); 
     void Visit(CThing cThing); 
     void Visit(DThing dThing); 
    } 

    interface ITransformedThing { } 

    class ATransformedThing : ITransformedThing { public ATransformedThing(AThing aThing, int arg) { } } 
    class BTransformedThing : ITransformedThing { public BTransformedThing(BThing bThing, int arg) { } } 
    class CTransformedThing : ITransformedThing { public CTransformedThing(CThing cThing, int arg) { } } 
    class DTransformedThing : ITransformedThing { public DTransformedThing(DThing dThing, int arg) { } } 

    class GetTransformedThing : IThingOperation 
    { 
     public int Arg { get; set; } 

     public ITransformedThing Result { get; private set; } 

     public void Visit(IThing iThing) { Result = null; } 
     public void Visit(AThing aThing) { Result = new ATransformedThing(aThing, Arg); } 
     public void Visit(BThing bThing) { Result = new BTransformedThing(bThing, Arg); } 
     public void Visit(CThing cThing) { Result = new CTransformedThing(cThing, Arg); } 
     public void Visit(DThing dThing) { Result = new DTransformedThing(dThing, Arg); } 
    } 

    interface IThing {} 
    class Thing : IThing {} 
    class AThing : Thing {} 
    class BThing : Thing {} 
    class CThing : Thing {} 
    class DThing : Thing {} 
    class EThing : Thing { } 

    class Program 
    { 
     static void Main(string[] args) 
     { 
      var things = new List<IThing> { new AThing(), new BThing(), new CThing(), new DThing(), new EThing() }; 
      var transformedThings = things.Select(thing => thing.GetTransformedThing(4)).Where(transformedThing => transformedThing != null).ToList(); 
      foreach (var transformedThing in transformedThings) 
      { 
       Console.WriteLine(transformedThing.GetType().ToString()); 
      } 
     } 
    } 
} 
+1

bạn konw, tôi thích ví dụ này nhưng bạn có thể đặt điểm của bạn nhiều sipler nhiều. –

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