2010-08-05 65 views
1

是否有可能做一些這樣的:繼承已經實例化的基本對象

public class ChildClass : BaseClass 
{ 
    public ChildClass(BaseClass o) 
    { 
     base = o; 
    } 
} 

基本上,我希望有一個透明的方式來包裝的其他功能裏面的基類。我想到的一個例子是自定義設置提供程序,它透明地審查通過它的設置。

public class SettingsAuditor : SettingsProvider 
{ 
    public SettingsAuditor(SettingsProvider o) 
    { 
     base = o; 
    } 

    public override void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals) 
    { 
     // Log the property change to a file 
     base.SetPropertyValues(context, propvals); 
    } 
} 

然後,我可以做到以下幾點:

mySettingsProvider = new SettingsAuditor(mySettingsProvider); 

所有更改會去通過重寫SetPropertyValues傳遞給原來的對象之前。

我可以用一個私人SettingsProvider成員,但後來我要麼無法從SettingsProvider繼承,或有一個完整的SettingsProviderbase)不被使用的。

我正在使用C#4.0和.Net 4.0。

+0

所以,你基本上想要一個透明的方式來包裝一個類,並允許所有的方法通過新的類重定向,而無需編寫自定義方法?您可能想要查看AOP或面向方面的編程http://en.wikipedia。org/wiki/Aspect-oriented_programming – 2010-08-05 14:10:31

+0

有點,如果我正確理解你的話。 – dlras2 2010-08-05 14:13:56

回答

4
  1. 你不能這樣做base = o;
  2. 你在找什麼是Decorator Pattern),這是一種組合方式在運行時添加功能(vs.遺產)。

而不是試圖設置base,你只是包含內部成員。只要包裝器實現與內部對象相同的接口或基類,就可以傳回新的包裝器。您可以根據需要包裝儘可能多的裝飾器。

考慮:

public interface ICar 
{ 
    void Drive(); 
} 

public class Car : ICar 
{ 
    public void Drive() 
    { 
     Console.WriteLine("vroom"); 
    } 
} 

public class BuckleUp : ICar 
{ 
    ICar car; 

    public BuckleUp(ICar car) { this.car = car; } 

    public void Drive() 
    { 
     Console.WriteLine("click!"); 
     car.Drive(); 
    } 
} 

public class CheckMirrors : ICar 
{ 
    ICar car; 
    public CheckMirrors(ICar car) { this.car = car; } 

    public void Drive() 
    { 
     Console.WriteLine("mirrors adjusted"); 
     car.Drive(); 
    } 
} 

現在考慮你有接受一個ICar並告訴它來驅動的方法。你可以給它一個Car,它可以工作,但你也可以將這輛車換成BuckleUpCheckMirrors,你根本不需要改變那個方法。您已通過使用Decorator Pattern進行合成來修改功能。

+0

我喜歡這種模式,但對於我不能控制的基類(例如'SettingsProvider'和'TcpSocket')來說,並不是完全可能的。我唯一能繼承的就是類本身,然後我被一個品牌每次都有新的基類,並且無法環繞現有對象。 – dlras2 2010-08-05 14:15:40

+1

@Daniel True,在某些情況下,它不起作用,就像封閉類一樣。但是,通常情況下,您可以將所有調用重定向到裝飾對象。例如,如果'ICar'接口具有'int Mileage'屬性,那麼'BuckleUp'和'CheckMirrors'都會實現如下屬性:'get {return car.Mileage; } set {car.Mileage = value; }'你可以對任何非密封的類做同樣的事情,但是如果基類成員沒有被聲明爲'virtual',你可能必須聲明成員爲'new'。 – Jay 2010-08-05 14:26:27

2

不,這看起來應該是一個組合vs繼承問題。你需要評估你是「一個」還是「有一個」。

A little help for your journey

+0

我明白'SettingsAuditor'在技術上「有一個''SettingsProvider',但我覺得它對開發者應該是透明的,應該像使用'SettingsProvider'一樣使用它。另一個問題是套接字 - 通常,我寫的類有一個「TcpSocket」,但它們公開了所有相同的方法,但帶有額外的邏輯,並且本身就像「TcpSocket」一樣。 – dlras2 2010-08-05 14:01:27

+0

+1的區別,但和鏈接。 – dlras2 2010-08-05 14:04:16

0

沒有,但你可以假的吧:在這裏

public class SettingsAuditor 
{ 
    SettingsProvider @base; 

    public SettingsAuditor(SettingsProvider o) 
    { 
     @base = o; 
    } 

    public void SetPropertyValues(SettingsContext context, SettingsPropertyValueCollection propvals) 
    { 
     // Log the property change to a file 
     @base.SetPropertyValues(context, propvals); 
    } 
} 

注意,@base是不實際的基礎,只是命名爲varaible base

+0

再次,'mySettingsProvider = new SettingsAuditor(mySettingsProvider);'不適用於此解決方案。 – dlras2 2010-08-05 14:02:20

2

這不是一個完整的暗示,它可能可以用表達式樹做得更乾淨......但這是使用DynamicObject和.Net 4.0來僞造AOP的一個快速動作。

public class MyDynamicWrapper<T> : DynamicObject 
{ 
    public T Wrapped { get; private set; } 
    public Action<T> Pre { get; private set; } 
    public Action<T> Post { get; private set; } 


    public MyDynamicWrapper(T wrapped, Action<T> pre, Action<T> post) 
    { 
     this.Wrapped = wrapped; 
     this.Pre = pre; 
     this.Post = post; 
    } 

    public override bool TryGetMember(
     GetMemberBinder binder, 
     out object result) 
    { 
     var type = typeof(T); 
     var method = type.GetMethod(binder.Name); 
     if (method != null) 
     { 
      Func<object> func =() => 
      { 
       if (Pre != null) 
        Pre(Wrapped); 

       // support for input parameters could be added here 
       var ret = method.Invoke(Wrapped, null); 

       if (Post != null) 
        Post(Wrapped); 
       return ret; 
      }; 
      result = func; 
      return true; 
     } 

     return base.TryGetMember(binder, out result); 
    } 
} 
public class MyDynamicWrapper 
{ 
    public static MyDynamicWrapper<T> Create<T>(
     T toWrap, 
     Action<T> pre = null, 
     Action<T> post = null) 
    { 
     return new MyDynamicWrapper<T>(toWrap, pre, post); 
    } 
} 

public class MyObject 
{ 
    public void MyMethod() 
    { 
     Console.WriteLine("Do Something"); 
    } 
} 

class Program 
{ 
    static void Main() 
    { 
     var myobject = new MyObject(); 
     dynamic mydyn = MyDynamicWrapper.Create(
           myobject, 
           p => Console.WriteLine("before"), 
           p => Console.WriteLine("after")); 
     // Note that you have no intellisence... 
     // but you could use the old implmentation before you 
     // changed to this wrapped version. 
     mydyn.MyMethod(); 

     /* output below 
     before 
     Do Something 
     after 
     */ 
    } 
} 
+0

現在,這不支持提供給該方法的參數。但是這種支持可以很容易地添加。此外,如果這被改爲表達式樹,你應該能夠有更好的類型和方法解析。 – 2010-08-05 15:08:24