我可以从这两个函数中做一个泛型函数吗?

时间:2018-09-19 11:07:47

标签: c# generics

这是两个函数(类方法):

private static Vector2[] ConvertVector2(Vector2[] values)
{
    var maxX = float.MinValue;
    var maxY = float.MinValue;
    var minX = float.MaxValue;
    var minY = float.MaxValue;

    foreach (var point in values)
    {
        maxX = Mathf.Max(point.x, maxX);
        minX = Mathf.Min(point.x, minX);
        maxY = Mathf.Max(point.y, maxY);
        minY = Mathf.Min(point.y, minY);
    }

    var result = new Vector2 [4]
    {
        new Vector2(minX, minY),
        new Vector2(maxX, maxY),
        new Vector2(maxX, minY),
        new Vector2(minX, maxY),
    };

    return result;
}


private static Vector3[] ConvertVector3(Vector3[] values)
{
    var zValue = values[0].z;
    var maxX = float.MinValue;
    var maxY = float.MinValue;
    var minX = float.MaxValue;
    var minY = float.MaxValue;

    foreach (var point in values)
    {
        maxX = Mathf.Max(point.x, maxX);
        minX = Mathf.Min(point.x, minX);
        maxY = Mathf.Max(point.y, maxY);
        minY = Mathf.Min(point.y, minY);
    }

    var result = new Vector3 [4]
    {
        new Vector3(minX, minY, zValue),
        new Vector3(maxX, maxY, zValue),
        new Vector3(maxX, minY, zValue),
        new Vector3(minX, maxY, zValue),
    };

    return result;
}

其中Vector2Vector3是Unity游戏引擎中定义的结构。我是C#的新手,我想弄清楚是否可以使用通用函数在这两个函数中不重复大量代码。

3 个答案:

答案 0 :(得分:2)

简短版本

您可以将功能简化为:

private static Vector2[] ConvertVector2(Vector2[] values)
{
    var initial = new Stats2(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue);

    return values.Aggregate(initial,Stats2.Apply,
        acc => new [] {
            new Vector2(acc.MinX, acc.MinY),
            new Vector2(acc.MaxX, acc.MaxY),
            new Vector2(acc.MaxX, acc.MinY),
            new Vector2(acc.MinX, acc.MaxY),
        });
}

通过将比较和结果生成代码视为函数,并将它们作为参数传递给folding函数。 LINQ通过Enumarable.Aggregate方法实现了这种通用模式

长版

您可以使用LINQ的Enumerable.Aggregate在任何IEnumerable序列之上计算自定义聚合。集合将函数应用于序列中的每个元素,其参数是当前元素和上一个操作的结果。查看它的另一种方法是在当前元素和存储的结果之间的循环内执行操作。这就是您当前的代码已经执行的操作。

MinMax是专长,其聚合函数分别为MinMax

以下代码段使用Aggregate overload接受聚合函数最终结果选择器来生成结果数组。

由于懒惰,我使用了内置的Vector2类,但添加了Stats2类以使选择器更整洁。

struct Stats2
{
    public readonly float MaxX;
    public readonly float MinX;
    public readonly float MaxY;
    public readonly float MinY;

    public Stats2(float maxX, float minX, float maxY, float minY)
        => (MaxX, MinX, MaxY, MinY) = (maxX, minX, maxY, minY);
}

var values = new [] 
{
    new Vector2(1,1),
    new Vector2(2,2),
    new Vector2(3,3),
    new Vector2(4,1),
};

ConnvertVector2可以这样写:

private static Vector2[] ConvertVector2(Vector2[] values)
{
    var initial = new Stats2(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue);

    return values.Aggregate(initial,
        (acc, point) => new Stats2(
                Math.Max(point.X, acc.MaxX),
                Math.Min(point.X, acc.MinX),
                Math.Max(point.Y, acc.MaxY),
                Math.Min(point.Y, acc.MinY)),
        acc => new [] {
                new Vector2(acc.MinX, acc.MinY),
                new Vector2(acc.MaxX, acc.MaxY),
                new Vector2(acc.MaxX, acc.MinY),
               new Vector2(acc.MinX, acc.MaxY),
        });
}

ConvertVector3在结果选择器中执行相同的计算,但差别很小,该结果选择器使用第一个元素的Z值:

    private static Vector3[] ConvertVector3(Vector3[] values)
    {
        var zValue = values[0].Z;
        var initial = new Stats2(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue);
        return values.Aggregate(initial,
            (acc, point) => new Stats2(
                    Math.Max(point.X, acc.MaxX),
                    Math.Min(point.X, acc.MinX),
                    Math.Max(point.Y, acc.MaxY),
                    Math.Min(point.Y, acc.MinY)),
            acc => new [] {
                  new Vector3(acc.MinX, acc.MinY,zValue),
                  new Vector3(acc.MaxX, acc.MaxY,zValue),
                  new Vector3(acc.MaxX, acc.MinY,zValue),
                  new Vector3(acc.MinX, acc.MaxY,zValue),
            });
    }

一项改进可能是将聚合函数移至Stats2本身:

struct Stats2
{
    public readonly float MaxX;
    public readonly float MinX;
    public readonly float MaxY;
    public readonly float MinY;

    public Stats2(float maxX, float minX, float maxY, float minY)
        => (MaxX, MinX, MaxY, MinY) = (maxX, minX, maxY, minY);

    public static Stats2 Apply(Stats2 acc,Vector2 point) =>
        new Stats2(  Math.Max(point.X, acc.MaxX),
                     Math.Min(point.X, acc.MinX),
                     Math.Max(point.Y, acc.MaxY),
                     Math.Min(point.Y, acc.MinY));

    public static Stats2 Apply(Stats2 acc,Vector3 point) =>
        new Stats2(  Math.Max(point.X, acc.MaxX),
                     Math.Min(point.X, acc.MinX),
                     Math.Max(point.Y, acc.MaxY),
                     Math.Min(point.Y, acc.MinY));
}

ConvertVector2可以简化为:

private static Vector2[] ConvertVector2(Vector2[] values)
{
    var initial = new Stats2(float.MinValue, float.MaxValue, float.MinValue, float.MaxValue);

    return values.Aggregate(initial,Stats2.Apply,
        acc => new [] {
            new Vector2(acc.MinX, acc.MinY),
            new Vector2(acc.MaxX, acc.MaxY),
            new Vector2(acc.MaxX, acc.MinY),
            new Vector2(acc.MinX, acc.MaxY),
        });
}

答案 1 :(得分:1)

我认为使用泛型是没有道理的,因为Vector2Vector3都没有实现有用的通用基类或接口。

鉴于这两个功能之间的唯一区别是3D版本为所有结果向量设置了固定的Z值,我只是将逻辑保留在2D函数中,然后从3D中重复使用它就可以设置Z值,例如:

private static Vector2[] ConvertVector2(Vector2[] values)
{
    var maxX = float.MinValue;
    var maxY = float.MinValue;
    var minX = float.MaxValue;
    var minY = float.MaxValue;

    foreach (var point in values)
    {
        maxX = Mathf.Max(point.x, maxX);
        minX = Mathf.Min(point.x, minX);
        maxY = Mathf.Max(point.y, maxY);
        minY = Mathf.Min(point.y, minY);
    }

    var result = new Vector2 [4]
    {
        new Vector2(minX, minY),
        new Vector2(maxX, maxY),
        new Vector2(maxX, minY),
        new Vector2(minX, maxY),
    };

    return result;
}

private static Vector3[] ConvertVector3(Vector3[] values) =>
   ConvertVector2(
      values.Select(v => new Vector2(v.X, v.Y)).ToArray()) // Convert to 2D
   .Select(v => new Vector3(v.X, v.Y, values[0].Z))
   .ToArray();

请注意,您可以简化ConvertVector2函数;例如:

private static Vector2[] ConvertVector2(Vector2[] values)
{
    var xs = values.Select(v => v.X); // .ToArray() for efficiency
    var ys = values.Select(v => v.Y); // .ToArray() for efficiency
    var min = new Vector2(xs.Min(), ys.Min());
    var max = new Vector2(xs.Max(), ys.Max());
    // If your Vector2 implements IComparable, use instead:
    // var min = values.Min();
    // var max = values.Max();

    return new[]
    {
        new Vector2(min.X, min.Y),
        new Vector2(max.X, max.Y),
        new Vector2(max.X, min.Y),
        new Vector2(min.X, max.Y),
    };
}

请注意,我在这里使用System.Numerics.Vector*,根据需要调整到您的Unity类(例如,小写的X和Y属性)。

答案 2 :(得分:-2)

一种更优雅的方法是改为使用扩展程序。

public static class VectorExtensions
{
    public static Vector2[] ToMaxVector(this Vector2[] values)
    {
        var maxX = float.MinValue;
        var maxY = float.MinValue;
        var minX = float.MaxValue;
        var minY = float.MaxValue;

        foreach (var point in values)
        {
            maxX = Mathf.Max(point.x, maxX);
            minX = Mathf.Min(point.x, minX);
            maxY = Mathf.Max(point.y, maxY);
            minY = Mathf.Min(point.y, minY);
        }

        var result = new Vector2[4]{
            new Vector2(minX, minY), 
            new Vector2(maxX, maxY), 
            new Vector2(maxX, minY), 
            new Vector2(minX, maxY) 
        };

        return result;
    }
}

通过这种方式,您可以减少实现方面的摩擦,因为它被添加到智能感知中,以简化实现。

它看起来仍然像是重复的代码,但是它是用于两个不同事物的代码。由于您是类型安全的,因此它的可测试性也容易得多。使用泛型,您会遇到很多不安全的类型问题。

然后您可以按如下方式使用它。

 var maxValues = vectors.ToMaxVector();
  • 其中vector是向量的数组。 “ Vector2 []”