簡體   English   中英

將通用輸入對象傳遞給方法C#

[英]Passing Generic Input object to a method C#

我有一個方法ABC()從我的應用程序中的兩個不同的地方調用。 從這兩個地方我都有不同的類對象,它實現了一個共同的接口“IDestination”。

我的兩個類和接口看起來像這樣:

public class ClassA: IDestination
{
        public string Var1 { get; set; }    
        public string Var2 { get; set; }    
        public string Var3 { get; set; }
}

public class ClassB: IDestination
{
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }
        public string Var4 { get; set; }
        public string Var5 { get; set; }
}

public interface IDestination
{
        string Var1 { get; set; }
        string Var2 { get; set; }
}

截至目前方法ABC()接受classA的對象,我希望它也可以接受classB的對象。 為此我已經使我的方法定義通用如下:

public string ABC<T>(T obj)
{

}

但是,問題在於我想要訪問類的屬性(classA和classB兩者)的ABC方法。

public string ABC<T>(T obj)
 {
        //Some code
        obj.var2; //of classA

        obj.var4; //of classB
        //Some code
 }

我不能允許在界面上做任何更改。

我怎樣才能做到這一點? 我不想創建另一種處理不同類對象的方法。 任何的想法?

首先, 接口不是繼承的,而是實現的

另一方面,可以約束通用參數:

public string ABC<T>(T obj) where T : IDestination

OP說:

我不能允許在界面上做任何更改。

順便說一句,根據您的要求和通用類型約束,您將無法同時接受classAclassB因為C#不支持多繼承。 如果要訪問classB成員,則需要擴展IDestination接口以定義要在類型為IDestinationclassB訪問的所有屬性:

public interface IDestination
{
      string Var1 { get; set; }
      string Var2 { get; set; }
      string Var3 { get; set; }
      string Var4 { get; set; }
      string Var5 { get; set; } 
}

或者,您可以使用其余屬性定義第二個接口:

public interface IDestination2
{
      string Var3 { get; set; }
      string Var4 { get; set; }
      string Var5 { get; set; } 
}

...你將在classB上實現它:

public class ClassB: IDestination, IDestination2

無論如何,這里的問題是你不能約束泛型參數來接受某些給定類的兩個不同的繼承。 也就是說,例如,如果您將T約束為IDestinationIDestination2 ,則您將無法將ClassA作為參數,因為它不實現IDestination2

OP說:

我不想創建另一種處理不同類對象的方法。

實際上這也可以在沒有泛型但是方法重載的情況下解決,即使你不想走這條路線也不是一個邪惡的方法:

public void ABC(IDestination destination) {}
public void ABC(IDestination2 destination) {}

// or directly...

public void ABC(ClassA destination) {}
public void ABC(ClassB destination) {}

否則,您需要按如下方式實現:

public string ABC<T>(T obj) where T : IDestination
{
     ClassA a = obj as ClassA;
     ClassB b = obj as ClassB;

     // Now if you want to access Var1, Var2 you can access them
     // using "obj" reference, because T is IDestination
     string var1 = obj.Var1;
     string var2 = obj.Var2;

     if(a != null) 
     {
         // Here access all ClassA members...
     }

     if(b != null)
     {
         // Here access all ClassB members...
     }
}

對我來說,上述方法是一個設計缺陷。 如果我使用泛型類型和接口是因為我想使用與泛型類型參數的類型相等的對象。

如果我開始對接口的特定實現執行向下轉換 ,就像擊敗使用IDestination的目的一樣,看起來你的方法可能只是接受object因為你的方法將訪問ClassAClassB成員而不是IDestination

public string ABC(object obj)
{
     ClassA a = obj as ClassA;
     ClassB b = obj as ClassB;

     // Now if you want to access Var1, Var2 you can access them
     // using "obj" reference, because T is IDestination
     string var1 = obj.Var1;
     string var2 = obj.Var2;

     if(a != null) 
     {
         // Here access all ClassA members...
     }

     if(b != null)
     {
         // Here access all ClassB members...
     }

     return "";
}

TL; DR

總之,您應該使用接口來鍵入對象和通用約束,以保證在引用上最小化輸入以避免強制轉換。 否則,通用打字不是您的解決方案。

你應該明確地重新思考你的設計。 當您的方法接受接口的實例時,它應該適用於所有類型,而不僅僅是一個集合。 想象一下,您創建了另一個也實現接口的類型。 您必須重新實現整個方法來支持它。 因此,應該在接口而不是類級別定義所有屬性,然后可以在方法中訪問它們。

但是,如果您真的必須使用當前的方法,則只需將其強制轉換為適當的類型:

ClassA a = obj as ClassA;
if (a != null) a.Var2 = ...
// this will fail if a user provides an instance of ClassC which also implements the interface
else ((ClassB)obj).Var4 = ...   

為此,您還需要對gegernic參數進行約束:

public string ABC<T>(T obj) where T : IDestination

我認為更好的設計如下:

public string ABC<T>(T obj) where T : IDestination
{

}

我這樣說,因為你想要一個實現IDestination接口的對象的泛型方法,所以它作為一個設計會更好,如果你已經將它聲明為約束。 為此,請看這里

然后,為了在ABC訪問所需的字段,接口應具有所有這些屬性。 因此,如果我們假設您希望類AB都具有相同的三個屬性Var1Var2Var3並且您想要在ABC訪問它們,那么您必須重新聲明您的界面,如下所示:

public interface IDestination
{
    string Var1 { get; set; }
    string Var2 { get; set; }
    string Var3 { get; set; }
}

我認為在泛型方法中很難處理特定於類型的屬性,因為它不知道這些屬性的具體內容。 我建議將這個邏輯委托給一個方法,這個類將通過接口實現。

正如您在評論中提到的,您無法更改界面IDestination並且您不希望破壞兼容性,因為此解決方案已在許多地方使用。 所以這個命題不是改變已經工作的ClassAClassB邏輯,而是添加新的接口實現,它將覆蓋你想在ABC方法中實現的這種類型條件邏輯。

我會添加另一個接口,它保存方法執行所有類型條件邏輯,並在ABC類的中間調用它。 為了保持您的泛型類型需要實現IDestination和IDestinationLogic接口的約束。 當然ABC方法可能需要稍作修改,但很難說它看起來有多精確,因為我們完全不了解它應該做什么。

示例實現可能如下所示:

public class GenericMethodClass
{

    public string ABC<T>(T obj) where T : IDestination, IDestinationLogic
    {
        var result = string.Empty;

        //some code happens here

        var typeConditionalLogicResult = obj.DoSomething(); 

        // do more stuff with according to the result of type-specific calculations

        return result;
    }
}


public class ClassA: IDestination, IDestinationLogic
{
    public string Var1 { get; set; }    
    public string Var2 { get; set; }    
    public string Var3 { get; set; }

    public string DoSomething()
    {
        return Var2;        
    }
}

public class ClassB: IDestination, IDestinationLogic
{
    public string Var1 { get; set; }
    public string Var2 { get; set; }
    public string Var3 { get; set; }
    public string Var4 { get; set; }
    public string Var5 { get; set; }

    public string DoSomething()
    {
        return Var4;        
    }
}

public interface IDestination
{
    string Var1 { get; set; }
    string Var2 { get; set; }   
}

public interface IDestinationLogic
{
    string DoSomething();   
}

我建議使用接口的方法與模板方法設計模式非常相似,模板方法設計模式最初包含抽象類。 您可以在這里閱讀: http//www.dofactory.com/net/template-method-design-pattern也許它會為您的案例增添更多亮點

好吧,要實現你想要的,你需要實現ABC方法來接受一種IDestination:

public string ABC<T>(T obj) where T : IDestination

但要實現這一點,您必須更改界面以獲得要訪問的所有方法:

    public interface IDestination
    {
        string Var1 { get; set; }
        string Var2 { get; set; }
        string Var3 { get; set; }
        string Var4 { get; set; }
        string Var5 { get; set; }
    }

然后你必須改變你的類來實現接口的所有成員,這意味着你可能會得到這樣的結果:

    public class ClassA : IDestination
    {
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }

        public string Var4
        {
            get
            {
                throw new NotImplementedException();
            }

            set
            {
                throw new NotImplementedException();
            }
        }

        public string Var5
        {
            get
            {
                throw new NotImplementedException();
            }

            set
            {
                throw new NotImplementedException();
            }
        }
    }

    public class ClassB : IDestination
    {
        public string Var1 { get; set; }
        public string Var2 { get; set; }
        public string Var3 { get; set; }
        public string Var4 { get; set; }
        public string Var5 { get; set; }
    }

但是如果它們只拋出一個異常,那么在ClassA中實現額外的方法並沒有多大意義。 所以你不妨完全實現它們。 但是ClassA和ClassB是完全相同的。 所以你必須問問自己,每個班級的獨特之處是什么才能證明IDestination的單獨具體實施? 回答這個問題將指導您做什么。

編輯:由於您無法更改界面,您是否被迫在方法ABC中使用界面? 你能創建一個ClassA和ClassB都繼承的抽象類嗎?

public abstract class ClassAB : IDestination //might not need the interface
{
  public virtual string Var1 { get; set; }
  public virtual string Var2 { get; set; }
  public virtual string Var3 { get; set; }
  public virtual string Var4 { get; set; }
  public virtual string Var5 { get; set; }
}

public class ClassA : ClassAB
{
   //override any of the virtual methods needed
}

public class ClassB : ClassAB
{
   //override any of the virtual methods needed
}

然后改變ABC方法:

public string ABC(ClassAB classAB)
{
  //all the methods are available on classAB
}

ClassA和ClassB仍然是IDestination類型,因為抽象父類實現它。 唯一的問題是,如果你可以改變方法ABC以期望一種類型的ClassAB而不是IDestination?

您可以使用反射類來處理該問題,您可以在運行時獲取參數的屬性,也可以使用。

Type type = typeof(obj);
PropertyInfo[] propertyList = type.GetProperties();

您可以使用Reflection獲取這些值,如下所示:

public static string ABC<T>(T obj) 
    {
        string s=string.Empty;
        //Some code
        if(obj is ClassA)
            s = obj.GetType().GetProperty("Var2").GetValue(obj, null).ToString(); //of classA

        if (obj is ClassB)
        s = obj.GetType().GetProperty("Var4").GetValue(obj,null).ToString(); //of classB
        //Some code
        return s;
    }

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM