2017-02-24 82 views
4

複製代碼我有這些結構如何避免訪問屬性,而無需使用反射

class A : IFoo { 
    Foo(){ 
     //do something 
    } 
    IList<B> Bs {get;set;} 
    IList<C> Cs {get;set;} 
} 

class B : IFoo { 
    Foo(){ 
     //do something 
    } 
    IList<D> Ds {get;set;} 
} 

class C : IFoo { 
    Foo(){ 
     //do something 
    } 
} 

class D : IFoo { 
    Foo(){ 
     //do something 
    } 
} 

class Helper{ 
    Bar(A a){ 
    a.Foo(); 
    Bar(a.Bs); 
    Bar(a.Cs); 
    } 

    Bar(IList<B> bs) 
    { 
    foreach(var b in bs) 
    { 
     Bar(b); 
    } 
    } 

    Bar(B b) 
    { 
    b.Foo(); 
    Bar(b.Ds); 
    } 

    Bar(IList<C> cs) 
    { 
    foreach(var c in cs) 
    { 
     Bar(c); 
    } 
    } 

    Bar(C c) 
    { 
    c.Foo(); 
    } 

    Bar(IList<D> ds) 
    { 
    foreach(var d in ds) 
    { 
     Bar(d); 
    } 
    } 

    Bar(D d) 
    { 
    d.Foo(); 
    } 
} 
interface IFoo { 
    void Foo(); 
} 

正如所看到的大量的代碼被重複Helper類(即方法Bar),用於不同類型的A,B,C,IList<A>,IList<B>,IList<C>。此外,當一個新的列表屬性添加到類我需要找回並更改助手類,它在某種程度上是與打開/關閉主要矛盾。

我知道如何使用Reflection來解決這個問題,但我正在尋找另一種聰明,整潔的方式來解決這個問題,而不是使用反射。

我可以自由地添加新Interface秒,但沒有基類

任何建議將讚賞

+0

看起來很複雜嗎?你想達到什麼目的?如果你不想'反射',可以使用'dynamic'或'expando object' – ANewGuyInTown

+0

@ANewGuyInTown即使動態問題仍然存在,我需要保留所有的Helper類結構,並在添加新類時複製粘貼代碼 – RezaRahmati

+0

'IFoo'接口上是否存在'Foo()'方法? – StuartLC

回答

1

假設方法Foo()存在的IFoo接口上,並給予您Bar方法B,C和除了調用該類的Foo實現外,爲什麼不完全刪除Bar過載(因爲依賴性耦合僅適用於接口方法Foo()

Y我們的環法變成(從Bar(IList<B/C/D> bs)改名):

void InvokeFoo(IEnumerable<IFoo> foos) 
{ 
    foreach(var foo in foos) 
    { 
     foo.Foo(); 
    } 
} 

這將被調用:

Bar(A a){ 
    a.Foo(); 
    InvokeFoo(a.Bs); 
    InvokeFoo(a.Cs); 
} 

編輯

以上回答忽略了事實類(如B)可以有子集合,通過它你也需要迭代。如果這些孩子集合在任意數量(例如,一個有兩個這樣的集合,B有1個,C和d無),並給予你的發言,你不能修改實際類ABC等,你可以指定爲每個班級採取'特殊'行動。這將是一個天真的實現,這不僅會遞歸的一個層次上工作(這是你所擁有的):

class Helper 
{ 
    public void Bar(A a) 
    { 
     a.Foo(); 
     InvokeFoo(a.Bs, foo => InvokeFoo(((B)foo).Ds)); 
     InvokeFoo(a.Cs); 
    } 

    void InvokeFoo(IEnumerable<IFoo> foos, Action<IFoo> childAction = null) 
    { 
     foreach (var foo in foos) 
     { 
      foo.Foo(); 
      childAction?.Invoke(foo); 
     } 
    } 
} 

使這個更通用/遞歸

因爲它似乎你是不是允許修改類(僅助手),違反固體開閉+本金似乎是不可避免的(因爲導航是由外部觀察者完成)。

由於polymorphicism的窮人的形式,並保持在一個地方的「氣味」,你可以做的是提供一個過載,以協助各類型步行它的孩子。默認的行爲(按照我的第一個代碼)僅僅是調用Foo,如果沒有類別是:

class Helper 
{ 
    private static readonly IDictionary<Type, Action<IFoo>> SpecialClassActions 
     = new Dictionary<Type, Action<IFoo>> 
     { 
      // Provide special handling for walking classes which have children 
      { 
       typeof(A), 
       foo => 
       { 
        InvokeChildFoo(((A)foo).Bs); 
        InvokeChildFoo(((A)foo).Cs); 
       } 
      }, 
      { 
       typeof(B), 
       foo => 
       { 
        InvokeChildFoo(((B)foo).Ds); 
       } 
      } 
      // Add more handling here as new subtypes are added 
     }; 

    static void WalkFooHierarchy(IFoo foo) 
    { 
     foo.Foo(); 

     Action<IFoo> specialChildAction; 
     if (SpecialClassActions.TryGetValue(foo.GetType(), out specialAction)) 
     { 
      specialChildAction(foo); 
     } 
    } 

    // Replaces your Bar(IList<> ..) methods 
    static void InvokeChildFoo(IEnumerable<IFoo> foos) 
    { 
     foreach (var foo in foos) 
     { 
      WalkFooHierarchy(foo); 
     } 
    } 

然後,通過開始從根級別的對象走路踢進程關閉:

public void Bar(A a) 
{ 
    Helper.WalkFooHierarchy(a); 
} 
+0

謝謝你的回答,那麼Foo怎麼叫D?(這是B上的一個屬性) – RezaRahmati

+0

是的 - 我錯過了B對其子D的遞歸。將重新思考+更新。 – StuartLC

+0

順便說一句,您的解決方案將減少名單的酒吧過載;) – RezaRahmati

1

如果你想保持OPEN/CLOSE原則不變,你可以去像這樣的東西 -

  • 每個班都會根據需要延長。
  • 新班級不需要修改舊班級Helper。所以實現變得有點像下面那樣。無論您添加多少班級,每個班級都有自己的FooBar實施。

示例 -

public interface IFoo 
{ 
    void Foo(); 
    void Bar(); 
} 

public abstract class BaseFoo : IFoo 
{ 
    public virtual void Foo() 
    { 
     //do nothing 
    } 

    public virtual void Bar() 
    { 
     //do nothing 
    } 
} 

public class A : BaseFoo //replace with IFoo if you do not want the base class, in that case implement both methods 
{ 
    IList<B> Bs { get; set; } 
    IList<C> Cs { get; set; } 

    public override void Bar() 
    { 
     base.Bar(); 

     foreach (var b in Bs) 
     { 
      b.Bar(); 
     } 

     foreach (var c in Cs) 
     { 
      c.Bar(); 
     } 
    } 
} 

public class B : BaseFoo //replace with IFoo if you do not want the base class, in that case implement both methods 
{ 
    IList<D> Ds { get; set; } 

    public override void Bar() 
    { 
     base.Bar(); 

     foreach (var d in Ds) 
     { 
      d.Bar(); 
     } 
    } 
} 

public class C : BaseFoo //replace with IFoo if you do not want the base class, in that case implement both methods 
{ 

} 

public class D : BaseFoo //replace with IFoo if you do not want the base class, in that case implement both methods 
{ 

} 

public static class Helper 
{ 
    public static void Bar(params IFoo[] foos) 
    { 
     foreach (var foo in foos) 
     { 
      foo.Bar(); 
     } 
    } 
} 
1

基於無價的建議和解決方案,我結束了類似下面,這個解決方案是開/關和加入另一隻列出該類應該改變,所有的重載BarHelper類被刪除。

class Program 
{ 
    static void Main(string[] args) 
    { 
     Helper.Bar(new A()); 
    } 
} 

public class A : IFoo, IMap 
{ 
    public A() 
    { 
     Cs = new List<C> { new C(), new C() }; 
     Bs = new List<B> { new B(), new B() }; 
    } 

    public void Foo() 
    { 
     Console.WriteLine("A"); 
    } 
    public List<B> Bs { get; set; } 
    public List<C> Cs { get; set; } 

    public List<Func<dynamic, List<IFoo>>> Map() 
    { 
     return this.CreateMap() 
      .Then(x => ((List<B>)x.Bs).ToList<IFoo>()) 
      .Then(x => ((List<C>)x.Cs).ToList<IFoo>()); 
    } 

} 

public class B : IFoo, IMap 
{ 
    public B() 
    { 
     Ds = new List<D> { new D(), new D() }; 
    } 
    public void Foo() 
    { 
     Console.WriteLine("B"); 
    } 

    public List<Func<dynamic, List<IFoo>>> Map() 
    { 
     return this.CreateMap() 
      .Then(x => ((List<D>)x.Ds).ToList<IFoo>()); 
    } 

    public List<D> Ds { get; set; } 
} 

public class C : IFoo, IMap 
{ 
    public void Foo() 
    { 
     Console.WriteLine("C"); 
    } 

    public List<Func<dynamic, List<IFoo>>> Map() 
    { 
     return this.CreateMap(); 
    } 
} 

public class D : IFoo, IMap 
{ 
    public void Foo() 
    { 
     Console.WriteLine("D"); 
    } 

    public List<Func<dynamic, List<IFoo>>> Map() 
    { 
     return this.CreateMap(); 
    } 
} 

public static class Mapper 
{ 
    public static List<Func<dynamic, List<IFoo>>> CreateMap(this IFoo item) 
    { 
     return new List<Func<dynamic, List<IFoo>>>(); 
    } 

    public static List<Func<dynamic, List<IFoo>>> Then(this List<Func<dynamic, List<IFoo>>> list, Func<dynamic, List<IFoo>> expression) 
    { 
     list.Add(expression); 
     return list; 
    } 
} 
public class Helper 
{ 
    public static void Bar(dynamic obj) 
    { 
     obj.Foo(); 
     var map = obj.Map(); 
     if (map != null) 
     { 
      foreach(var item in map) 
      { 
       var lists = item(obj); 
       foreach(var list in lists) 
       { 
        Bar(list); 
       } 
      } 
     } 
    } 

} 

public interface IFoo 
{ 
    void Foo(); 
} 

public interface IMap 
{ 
    List<Func<dynamic, List<IFoo>>> Map(); 
} 

@brainlesscoder,@StuartLC:因爲你的解決方案也是正確的,我只上投票支持他們,我不標註任何的解決方案,答案讓這個線程打開,可能別人提出一個更好的解決方案比我們。如果你認爲我的解決辦法也不錯,請贊成票;)

0

這在我看來是這樣做的最直接的方式:

public static class Helper 
{ 
    public static void Bar<F>(F f) where F : IFoo 
    { 
     f.Foo(); 
     Helper.Bar((f as A)?.Bs); 
     Helper.Bar((f as A)?.Cs); 
     Helper.Bar((f as B)?.Ds); 
    } 

    public static void Bar<F>(IList<F> fs) where F : IFoo 
    { 
     if (fs != null) 
     { 
      foreach (var f in fs) 
      { 
       Helper.Bar(f); 
      } 
     } 
    } 
} 

現在,如果代碼的其餘部分被定義像這樣:

public class A : IFoo 
{ 
    public void Foo() { Console.Write("A"); } 
    public IList<B> Bs { get; set; } = new List<B>() { new B(), new B(), }; 
    public IList<C> Cs { get; set; } = new List<C>() { new C(), new C(), }; 
} 

public class B : IFoo 
{ 
    public void Foo() { Console.Write("B"); } 
    public IList<D> Ds { get; set; } = new List<D>() { new D(), new D(), }; 
} 

public class C : IFoo 
{ 
    public void Foo() { Console.Write("C"); } 
} 

public class D : IFoo 
{ 
    public void Foo() { Console.Write("D"); } 
} 

public interface IFoo 
{ 
    void Foo(); 
} 

...那麼你就可以運行這個命令:

var a = new A(); 
var b = new B(); 
var c = new C(); 
var d = new D(); 

Helper.Bar(a); 
Helper.Bar(b); 
Helper.Bar(c); 
Helper.Bar(d); 

...,你會得到ABDDBDDCCBDDCD如預期的那樣輸出。

+0

謝謝,是的,結果如預期,唯一的問題是,當一個集合添加到類酒吧需要改變,並與OC – RezaRahmati

+0

@RezaRahmati矛盾 - 你能解釋一下,「當一個酒吧需要改變並與OC相抵觸的時候,這個酒吧被添加了嗎?」我不明白你的意思。 – Enigmativity

+0

我的意思是,例如,如果一個新的列表屬性被添加到D類,不僅D類需要更改,而且還有助手類,並且這是不服從開放/關閉主體 – RezaRahmati