2017-04-03 118 views
3

假設我有一個基類A,其子類爲B,C和D.我有一個類MyClass,它包含每個類的重載。我希望有一個計數器可以在任何時候增加這些方法之一,而不是在MyClass內部的任何地方調用。這可以通過幾個簡單的輔助方法來完成:C#避免重複邏輯

public class MyClass 
{ 
    int counter; 

    public doSomethingExternal(B b) {counter++; doSomething(b);} 
    public doSomethingExternal(C c) {counter++; doSomething(c);} 
    public doSomethingExternal(D d) {counter++; doSomething(d);} 

    private doSomething(B b) {...} 
    private doSomething(C c) {...} 
    private doSomething(D d) {...} 
} 

這真的會影響到我的開發。有沒有辦法更好的方式來編寫這些幫助器方法,以便我們對每個子類都沒有重複的邏輯?我設想的解決方案是這樣的:

// Magically, we know which type we are and call the correct doSomething 
public doSomethingExternal(A a) {counter++; doSomething(a);} 

我覺得這種泛化可以用反射來實現,但我聽說反射一般是緩慢的,可能是複雜的理解。我很好奇,如果有一個更傳統的模式,我忽略瞭解決這個邏輯重複問題。

+1

也許[模板方法模式](https://www.tutorialspoint.com/design_pattern/template_pattern.htm)會幫助你。 – ckruczek

+0

如果'A','B'和'C'共享一些共同的東西,比如一個接口,將會很有幫助。 – ja72

+0

我認爲'C#'7.0有模式匹配,可以幫助您選擇正確的方法來調用。 – ja72

回答

2

那麼你可以解決這個問題的一種方法是使類A,B和C實現一個接口。根據doSomething函數的作用,你可以做這樣的事情。

public interface IFoo 
{ 
    void DoSomething(); 
} 

然後你可以在MyClass中有一個公共函數。

public void doSomethingExternal(IFoo foo) 
{ 
    counter++; 
    foo.DoSomething(); 
} 
+1

這會將主要責任(對A,B,C類做某些事情)的'MyClass'移動到'A','B'和'C'類。如果'MyClass'什麼都不做,那麼你根本不需要'MyClass'。 – Fabio

+0

不完全是因爲MyClass有一個狀態,計數器。取決於doSomething方法在做什麼,他可以使用foo.DoSomething()並將MyClass發送到方法中(如果他願意的話)。 –

2

使用dynamic

public class MyClass 
{ 
    int counter; 

    public doSomethingExternal(A value) // A is base class 
    { 
     counter++; 
     dynamic dynamicValue = value; 
     doSomething(dynamicValue); // Correct overload will be used based on actual type 
    } 

    private doSomething(B b) {...} 
    private doSomething(C c) {...} 
    private doSomething(D d) {...} 
} 

離開的doSomethingExternal參數作爲一個基類,可以防止傳遞其他類型的方法的另一種方法。

-1

這是笨重的,它可以用C#7.0和模式匹配清理,但我還沒有。

public interface A { } 
public struct B :A { } 
public struct C :A { } 
public struct D: A { } 

public class MyClass 
{ 
    int counter; 
    private void DoSomething(B b) { ... } 
    private void DoSomething(C c) { ... } 
    private void DoSomething(D d) { ... } 

    public void DoSomethingExternal(A arg) 
    { 
     if (arg is B) 
     { 
      DoSomething((B)arg); 
     } 
     else if (arg is C) 
     { 
      DoSomething((C)arg); 
     } 
     else if (arg is D) 
     { 
      DoSomething((D)arg); 
     } 
     else 
     { 
      // If `A` is not of `B`, `C` or `D` types return without incrementing counter 
      return; 
     } 
     counter++; 
    } 
} 

編輯1

如果一個公共接口或A類不存在,那麼你必須使用object

public void DoSomethingExternal(object arg) 
{ 
} 
+0

您不在示例中使用「模式匹配」。模式匹配將檢查「模式」並提供結果實例,所以你的'if'語句應該看起來:'if(arg is B argB){DoSomething(argB); }' – Fabio

+0

@Fabio,因爲我還沒有2017年,我無法提供樣本。我希望別人會用C#7.0給出答案 – ja72

-1

這真的困擾dev的我。有沒有辦法更好的方式來編寫這些幫助器方法,以便我們對每個子類都沒有重複的邏輯?

爲什麼?實際上我發現你的代碼很容易閱讀。我不會真的考慮「重複代碼」,增加一個簡單的計數器並委託給一個私有方法。考慮發佈的所有替代方案?他們有沒有讓你的生活更輕鬆?

當然你可以利用c#7的模式匹配,但它真的值得嗎?

public void doSomethingExternal(A a) 
{ 
    counter++; 

    switch (a) 
    { 
     case B b: 
      doSomething(b); 
      break; 
     case C c: 
      doSomething(c); 
      break; 
     case D d: 
      doSomething(d); 
      break; 
     case null: 
      throw new ArgumentNullException(nameof(a)); 
     default: 
      throw new InvalidOperationException(); 
    } 
} 

這一切只是爲了避免重複counter++?我們可能只會看到一個非常簡化的代碼實際外觀以及「重複」邏輯的程度。