簡體   English   中英

在抽象基類的靜態方法中實例化幾個派生類中的任何一個(取決於參數)

[英]Instantiate any one of several derived classes (depending on an argument) within a static method of the abstract base class

我一直試圖找到一種優雅的方法來避免在我的所有派生類中重復代碼。 在這一點上,我不確定繼續的最佳方式。

我想在基類中編寫一個方法,該方法將實例化並使用其任何派生類,而無需在編寫新派生類時編輯該方法。

我曾嘗試學習/使用通用方法,但開始認為我可能會為此應用程序走向死胡同。 我知道使用反射可能很昂貴,而且由於這種方法旨在處理數百甚至數千個元素,因此這似乎是一個壞主意。

現在我正在考慮嘗試以某種方式將類本身作為參數傳遞......也許吧。 這在我看來也不完全正確。

我願意進行研究,但希望能幫助我指明正確的方向。

這是我所擁有的內容的刪節版...

基類:

public abstract class Element
{
    public string ElementName { get; }
    public List<string> BadParameters { get; set; } = new List<string>();

    //Constructor
    public Element(string name)
    {
        ElementName = name;
    }

    //The method in question---
    public static List<string> GetBadParameters(//derived class to instantiate)
    {
        var elem = new //derived class();
        return elem.BadParameters;
    }

}

派生類 1:

public class Wall : Element
{
    public double Length { get; set; }
    public bool LoadBearing { get; set; }

    //Constructor
    public Wall(string name): base(name)
    {
        SetBadParameters();
    }

    public void SetBadParameters()
    {
        BadParameters = //A wall specific way of setting bad parameters
    }
}

派生類 2:

public class Floor : Element
{
    public double Area { get; set; }
    public double Slope { get; set; }

    //Constructor
    public Floor(string name): base(name)
    {
        SetBadParameters();
    }

    public void SetBadParameters()
    {
        BadParameters = //A floor specific way of setting bad parameters
    }
}

執行:

public class Implementation
{
    public List<string> GetAllBadElementParameters()
    {
        List<string> output = new List<string>;

        List<string> badWalls = GetBadParameters(//Wall class)
        List<string> badFloors = GetBadParameters(//Floor class)

        output = output.AddRange(badWalls).AddRange(badFloors);
        return output;
    }
}

編輯 - 澄清:

的實際內容

public List<string> BadParameters

不要緊。 糟糕的參數,它們如何以及為什么糟糕,都是無關緊要的。

我想要完成的是避免在派生類中定義方法“GetBadParameters()”,因為此方法對於所有派生類都完全相同。

只有填充“BadParameter”基類屬性才能從一個派生類更改為另一個派生類。

編輯 2 - 我在基類中嘗試通用方法:

我知道這行不通,但它可能傳達了我希望發生的事情。

    public static List<string> GetAllBadParameters<T>(List<string> names) where T : ANY DERIVED CLASS, new()
    {
        List<string> output = new List<string>();

        foreach (string name in names)
        {
            var elem = new T(name);
            foreach (string badParameter in elem.BadParameters)
            { 
                output.Add(badParameter); 
            }
        }

        return output;
    }

嗯……首先,我猜“壞參數”是指 Element 派生類中的屬性名稱。 例如,我猜測如果牆的長度為負,那么“長度”將是該特定牆的錯誤參數。 其次,我猜測您將擁有大量元素,例如圖表或其他任何內容中的牆壁和地板(以及其他事物)的數量。

假設,那么一種方法是在 Element 類中有一個返回錯誤參數的抽象方法,並在每個派生類中實現它。 像這樣的東西:

public abstract class Element  
{
    public string Name { get; private set; }
    public abstract IList<string> GetBadParameters();
    public Element( string name) { this.Name = name; }
}

public class Wall 
{
    public Wall( string name): base(name) {}
    public double Length { get; set; }
    public bool IsLoadBearing { get; set; }

    public IList<string> GetBadParameters() {
         List<string> bad = new List<string>();
         if (this.Length <= 0) { bad.Add( this.Name + ": " + nameof( this.Length); }
         if (this.IsLoadBearing && this.Length > whatever) { bad.Add( this.Name + ": " + nameof( this.IsLoadBearing); }
         return bad;
    }
}

然后,如果你有一個所有元素的列表,你可以通過

IList<string> allBadParemeters = elements.SelectMany( e => e.GetBadParameters() ); 

不過我想說的是,這可能不是一個很棒的設計。 您最終會得到一個系統,其中許多元素都包含錯誤的參數。 如果您首先防止不良參數的發生,生活可能會容易得多。 您可以通過將所有參數屬性的“設置”方法設為私有並添加諸如 bool Wall.TrySetParameters( double length, bool isLoadBearing) 之類的方法來實現此目的。 如果參數不好,那么這只會返回 false 而不會將參數分配給牆。 如果您想在基類中使用 TrySetParameters,那么您可以使用更通用的簽名,例如

 public struct Parameter { 
     public Parameter( string name, object value) { … } 
     public string Name { get; private set; }
     public object Value { get; private set; }
 }

 abstract public class Element { 
    …
    abstract public bool TrySetParameters( params Parameter[] parameters); 
 }

我假設您的 BadParameter 列表內容對於所有派生類都是相同的。 如果此列表不常見,則在 Base Class 中填寫這些列表沒有意義。 根據這個假設,我可以建議您進行以下更改。

您的基類看起來像這樣。 無需將 GetBadParameters() 設為靜態

public abstract class Element
    {
        public string ElementName { get; }
        public List<string> BadParameters { get; set; } = new List<string>();

        //Constructor
        public Element(string name)
        {
            ElementName = name;
        }

        /// <summary>
        /// Tjis Method is common for alal derived classes. Assuming content is same for all dervied class
        /// </summary>
        /// <returns></returns>
        //The method in question---
        public List<string> GetBadParameters()
        {
            return new List<string>() { "1", "2" };
        }

    }

您的第一個派生類 Wall,它將從 base 調用 GetBadParameters。

 public class Wall : Element
    {
        public double Length { get; set; }
        public bool LoadBearing { get; set; }

        //Constructor
        public Wall(string name) : base(name)
        {
            SetBadParameters();
        }

        public void SetBadParameters()
        {
            BadParameters = GetBadParameters();//Calling base GetBadParameters
        }
    }

與第二個派生類“Floor”相同

 public class Floor : Element
    {
        public double Area { get; set; }
        public double Slope { get; set; }

        //Constructor
        public Floor(string name) : base(name)
        {
            SetBadParameters();
        }

        public void SetBadParameters()
        {
            BadParameters = GetBadParameters();//Calling base GetBadParameters
        }
    }

在您的實現類中,您可以通過將 Element 類作為參考並調用相應的 GetBadParameters 來創建牆壁和地板對象

public class Implementation
    {
        public List<string> GetAllBadElements()
        {
            List<string> output = new List<string>;

            Element _wall = new Wall("wall");
            Element _floor = new Floor("floor");
            List<string> badWalls = _wall.GetBadParameters(); //Returns Wall bad Parameters
            List<string> badFloors = _floor.GetBadParameters(); //Returns Floor bad Parameters

            output = output.AddRange(badWalls).AddRange(badFloors);
            return output;
        }
    }

您試圖從List<string>返回不同類型的參數,這是不可能的。 List<string>不知道哪個。 您的基類不應具有這樣的方法。 那應該在您的實現中完成。 List<Element>可以包含繼承Element基類(Wall,Floor,..)的任何對象。 然后,可以通過元素列表的OfType(DerivedType) IEnumerable方法選擇要處理的類型。

以下幾點可能會有所幫助:

基類

public abstract class Element
{
    private readonly List<string> Parameters = new List<string>();

    public Element(string name) => ElementName = name;

    public string ElementName { get; }

    public void SetParameter(string Param)
    {
        if (Param != null)
            Parameters.Add(Param);
    }

    public void SetParameters(IEnumerable<string> Params)
    {
        if (Params != null)
            Parameters.AddRange(Params);
    }

    public IEnumerable<string> GetParameters()
    {
        return Parameters;
    }

    public static Element CreateElement<T>(string elementName, IEnumerable<string> Params) where T : Element
    {
        switch (typeof(T))
        {
            case var type when type == typeof(Wall):
                var wall = new Wall(elementName);
                wall.SetParameters(Params);
                return wall;
            case var type when type == typeof(Floor):
                var floor = new Floor(elementName);
                floor.SetParameters(Params);
                return floor;
            default:
                return null;
            case null:
                throw new ArgumentNullException(nameof(T));
        }
    }

    public override string ToString()
    {
        return $"{ElementName}: {string.Join(", ", Parameters)}";
    }
}

派生類

public class Wall : Element
{
    public double Length { get; set; }
    public bool LoadBearing { get; set; }

    public Wall(string name) : base(name) { }
}

public class Floor : Element
{
    public double Area { get; set; }
    public double Slope { get; set; }

    public Floor(string name) : base(name) { }
}

實施實例

var wall1 = new Wall("Wall 1");
wall1.SetParameter("Wall 1 Parameter 1");
wall1.SetParameters(new[] { "Wall 1 Parameter 2", "Wall 1 Parameter 3" });

var wall2 = Element.CreateElement<Wall>("Wall 2", new[] { "Wall 2 Param 1", "Wall 2 Param 2", "Wall 2 Param 3" });

var floor1 = new Floor("Floor 1");
floor1.SetParameter("Floor 1 Parameter 1");
floor1.SetParameters(new[] { "Floor 1 Parameter 2", "Floor 1 Parameter 3" });

var floor2 = Element.CreateElement<Floor>("Floor 2", new[] { "Floor 2 Param 1", "Floor 2 Param 2", "Floor 2 Param 3" });

var elements = new List<Element>
{
    wall1,
    floor1,
    wall2,
    floor2,
};

foreach (Element element in elements)
{
    Console.WriteLine(element.GetType().Name);

    Console.WriteLine(string.Join(",", element.GetParameters()));
}

var walls = elements.OfType<Wall>();
var floors = elements.OfType<Floor>();

walls.ToList().ForEach(w => Console.WriteLine(w.ElementName));
floors.ToList().ForEach(f => Console.WriteLine(f.ElementName));

var wallsParameters = elements.OfType<Wall>().SelectMany(w => w.GetParameters());
var floorsParameters = elements.OfType<Floor>().SelectMany(f => f.GetParameters());
var allParameters = elements.SelectMany(p => p.GetParameters());

wallsParameters.ToList().ForEach(w => Console.WriteLine(w));
floorsParameters.ToList().ForEach(f => Console.WriteLine(f));
allParameters.ToList().ForEach(p => Console.WriteLine(p));

祝好運。

暫無
暫無

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

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