簡體   English   中英

C# 中的 generics 太多

[英]Too many generics in C#

我正在編寫一個用於數學優化的算法,並且面臨大量泛型參數的問題。 有解決方案、問題、算子和算法。 每個都有自己的接口結構,因此可以用新的類進行擴展。 使用在 Main function 中創建變異算子 NonUniform 的示例,我展示了其創建的復雜性。 如果您很少創建此類對象,這不是問題,但是有許多類似的運算符,並且對於每個,您需要列出所有通用參數,並且它們也會重復。 我嘗試將這些參數傳遞給方法,但是在 inheritance 期間,無法指定更嚴格的限制。 如何擺脫這么多的generics? 也許值得修改架構,或者有一些我不知道的語言機制。

#region Algorithms

interface IAlgorithm<out TProblem, out TSolution>
    where TProblem : IProblem<TSolution>
    where TSolution : ISolution
{
    Random Random { get; }
    TProblem Problem { get; }
    TSolution Best { get; }
}

interface IIterativeAlgorithm<out TProblem, out TSolution>
    : IAlgorithm<TProblem, TSolution>
    where TProblem : IProblem<TSolution>
    where TSolution : ISolution
{
    int CurrentIteration { get; }
}

class IterativeAlgorithm<TProblem, TSolution>
    : IIterativeAlgorithm<TProblem, TSolution>
    where TProblem : IProblem<TSolution>
    where TSolution : ISolution
{
    public Random Random { get; }
    public TProblem Problem { get; }
    public TSolution Best { get; }
    public int CurrentIteration { get; }
}

#endregion

#region Operator

interface IOperator<in TAlgorithm, TProblem, TSolution>
    where TAlgorithm : IAlgorithm<TProblem, TSolution>
    where TProblem : IProblem<TSolution>
    where TSolution : ISolution
{
    public void SetAlgorithm(TAlgorithm algorithm);
}

interface ICoordinateMutator<in TAlgorithm, TFunction, TPoint>
    : IOperator<TAlgorithm, TFunction, TPoint>
    where TAlgorithm : IAlgorithm<TFunction, TPoint>
    where TFunction : IFunction<TPoint>
    where TPoint : IPoint
{
    void MutateCoordinates(TPoint point);
}

abstract class CoordinateMutator<TAlgorithm, TFunction, TPoint>
    : ICoordinateMutator<TAlgorithm, TFunction, TPoint>
    where TAlgorithm : IAlgorithm<TFunction, TPoint>
    where TFunction : IFunction<TPoint>
    where TPoint : IPoint
{
    protected Func<Random> GetRandom { get; set; }

    public CoordinateMutator()
    {
        GetRandom = null;
    }

    public virtual void SetAlgorithm(TAlgorithm algorithm)
    {
        GetRandom = () => algorithm.Random;
    }

    public abstract void MutateCoordinates(TPoint point);
}

class NonUniform<TIterativeAlgorithm, TFunction, TPoint> :
    CoordinateMutator<TIterativeAlgorithm, TFunction, TPoint>
    where TIterativeAlgorithm : IIterativeAlgorithm<TFunction, TPoint>
    where TFunction : IFunction<TPoint>
    where TPoint : IPoint
{
    private Func<int> GetCurrentIteration { get; set; }
    private Func<double[]> GetLowerSearchBorder { get; set; }
    private Func<double[]> GetUpperSearchBorder { get; set; }

    public override void SetAlgorithm(TIterativeAlgorithm algorithm)
    {
        base.SetAlgorithm(algorithm);
        
        GetCurrentIteration = () => algorithm.CurrentIteration;
        GetLowerSearchBorder = () => algorithm.Problem.LowerSearchBorders;
        GetUpperSearchBorder = () => algorithm.Problem.UpperSearchBorders;
    }

    public override void MutateCoordinates(TPoint point)
    {
        Random random = GetRandom();
        double currentIteration = GetCurrentIteration();
        double[] lowerSearchBorders = GetLowerSearchBorder();
        double[] upperSearchBorders = GetUpperSearchBorder();

        throw new NotImplementedException("TODO");
    }
}

#endregion

#region Problems

interface IProblem<in TSolution>
    where TSolution : ISolution
{
}

interface IFunction<in TPoint>
    : IProblem<TPoint>
    where TPoint : IPoint
{
    double[] LowerSearchBorders { get; }
    double[] UpperSearchBorders { get; }
}

abstract class Function<TPoint>
    : IFunction<TPoint>
    where TPoint : IPoint
{
    public double[] LowerSearchBorders { get; }
    public double[] UpperSearchBorders { get; }
}

class Sphere<TPoint>
    : Function<TPoint>
    where TPoint : IPoint
{
    public double GetValueOf(TPoint solution) => throw new NotImplementedException("TODO");
}

#endregion

#region Solutions

interface ISolution
{
}

interface IPoint : ISolution
{
}

class Point : IPoint
{
}

#endregion

private static void Main()
{
    var cm = new NonUniform<IterativeAlgorithm<Function<Point>, Point>, Function<Point>, Point>();
}

我認為您擁有太多 generics 的主要原因之一來自ICopy<T>接口。

您可以將其設為非泛型並使 Copy 方法返回 object。 或者改用 IClonable 接口。 它會減少其他接口和類的泛型參數,但每次調用 Copy 方法時都需要對其結果進行強制轉換。

另一種選擇是使用 C# 9 和 .NET 5 的協變返回功能

當接口繼承 ICopy 時,它不必是通用的

    interface ISolution : ICopy<ISolution>
    {
    }
    interface IPoint : ISolution
    {
    }

您需要聲明一個抽象的 class 實現接口:

    abstract class PointBase : IPoint
    {
        public abstract ISolution Copy();
    }

然后,您可以使用協變返回來實現具有更具體類型的 Copy 方法

    class Point : PointBase
    {
        public override Point Copy() => new Point();
    }

據我所知,不需要 generics。 例如

interface ISolution
{
}

這是干什么用的? 它沒有屬性或方法,所以我無法對解決方案做任何事情。 唯一的用法似乎是作為標記,例如依賴注入/反射,但您似乎沒有以這種方式使用它。

另一個例子是方法

public override void MutateCoordinates(TPoint point)
{
    Random random = GetRandom();
    double currentIteration = GetCurrentIteration();
    double[] lowerSearchBorders = GetLowerSearchBorder();
    double[] upperSearchBorders = GetUpperSearchBorder();

    throw new NotImplementedException("TODO");
}

我們知道那個點是一個IPoint ,它是一個ISolution 但那又怎樣? 我們不能將點用於任何事情,因為它沒有屬性或方法,“TODO”很方便地省略了一些東西。 我們可以將float X {get;} float Y {get;}之類的屬性添加到接口以允許實際使用,但我們也可以使用方法簽名

public override void MutateCoordinates(IPoint point)

使用 generics 的一個可能原因是它可能允許更多優化,但代價是更大的代碼大小。 如果您在點數百萬次上調用方法,這可能與點之類的東西相關,但可能與IAlgorithm等更高級別的概念無關。 分析和/或基准測試應該顯示這是否相關。 標准建議是從最簡單的解決方案開始。

我會考慮修改您的結構並刪除 generics 與常規類型的所有用法。 為這樣的領域創建一個好的描述可能非常困難,而且如果沒有更多信息,很難提供好的建議。 generics 可能是某種建模的最佳解決方案。 但是,如示例所示,用泛型類型替換所有類型可能不是 go 的方法。

暫無
暫無

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

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