【发布时间】:2021-03-25 09:02:23
【问题描述】:
我正在编写一个数学优化算法,并面临大量泛型参数的问题。有解决方案、问题、运算符和算法。每个都有自己的接口结构,因此可以用新的类进行扩展。使用在 Main 函数中创建变异算子 NonUniform 的示例,我展示了其创建的复杂性。如果您很少创建此类对象,这不是问题,但是有许多类似的运算符,并且对于每个,您需要列出所有通用参数,并且它们也会重复。我试图将这些参数传递给方法,但是在继承期间,无法指定更严格的限制。如何摆脱这么多仿制药?也许值得修改架构,或者语言的某些机制我不知道。
#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>();
}
【问题讨论】:
-
如果你觉得很难理解,那么我建议你将每个类分成一个单独的文件,一次专注于一个,让接口/泛型正确,并确保你写得好单元测试。您正在有效地创建一个库,而当您有很多事情要做时,这似乎令人生畏。
-
此代码专门呈现在一张纸上,以便您了解这些类是如何相互关联的。我还没有遇到扩展功能/架构和类之间交互的问题。问题是类,例如 NonUniform,由于大量重复的泛型,不方便创建。
-
所有泛型的目的是什么?是否只允许
Copy方法返回正确类型的对象?您是否考虑过其他解决方案?喜欢尽可能使用不变性,从而避免需要副本。或者只是返回一个对象,并让调用者在需要时转换为正确的类型。 -
泛型的目标是类型安全。也就是说,使用适当的类。例如,在 NonUniform 中,运算符应使用 IIterativeAlgorithm,而此类型的其他运算符可以简单地使用 IAlgorithm。我将从提供的代码中删除 ICopy 接口,因为它会分散问题的注意力。在 Main 方法中创建对象时,使用不变性无助于摆脱重复的泛型参数。
-
不需要泛型来确保类型安全,常规类型就足够了。泛型主要用于 1. 各种类型的容器,2. 优化,即编译器为每种特定类型生成代码,这可以改善内联,但也会导致代码体积膨胀。
标签: c# generics inheritance architecture