【问题标题】:Define which function to use according two parameters with an infinity of combinations in C#在 C# 中根据具有无限组合的两个参数定义要使用的函数
【发布时间】:2020-04-21 16:59:12
【问题描述】:

我有一个问题必须通过我不知道的设计模式来解决。这是用于不同坐标系之间的转换。我给你解释。

1/ 我有一个引用系统集合 (EPSG) 的枚举:

public enum System
{
    NAD27 = 3800,
    NAD83 = 3801,
    ...
    WGS84 = 4326
}

这些枚举允许定义对象坐标使用的系统。

public struct Coordinate
{
    public System system;

    public double X;
    public double Y;
    public double Z;
}

可以有无数个系统。枚举索引不一定相互跟随,也不能修改。

2/ 对象必须能够从一个系统传递到另一个系统。我将转换从一个系统应用到另一个系统。系统 A -> 系统 B。 此转换不会更改类型。它会更改坐标(x、y 和 z)的值,以便该点根据其系统位于正确的位置。

所有组合都是可能的。但是,根据启动系统的不同,改变系统的计算并不总是相同的。

3/ 我有几个包含转换方法的静态类:

public static class WGS84
{
    public static Coordinate ToNAD27 (double X, double Y, double Z)
    {
        ...
    }

    public static Coordinate ToNAD83 (double X, double Y, double Z)
    {
        ...
    }
}

我们来解决问题。

4/ 如果我第一次使用一个开关,将包含起始系统和到达系统的对象作为参数,则定义使用哪种转换并避免大量转换:

public class SystemTransform
{
    public System initial;
    public System terminal;
}


public struct Coordinate 
{
    public System system;

    public double X;
    public double Y;
    public double Z;


    public Coordinate Transform (SystemTransform s)
    {
        switch(s)
        {
            case SystemTransform st when st.Initial == System.WGS84 && st.Terminal == System.NAD27:
                        return WGS84.ToNAD27(X, Y, Z);

            ...

            case SystemTransform st when st.Initial == System.WGS84 && st.Terminal == System.Z:
                        throw new NotImplementedException ($ "{s} not implmented");
        }
    }
}

5/ 不切实际和审美,我翻了字典:

public static class TransformWithDictionary
{
        private static Dictionary <SystemTransform, Func <Coordinate , Coordinate >> ParserMap = new Dictionary <SystemTransform, Func <Coordinate , Coordinate >>
        {
            {new SystemTransform (System.WGS84, System.NAD27), coord => WGS84.ToNAD27(coord)},
            {new SystemTransform (System.WGS84, System.NAD83), coord => WGS84.ToNAD83(coord)},
        };

        public MyObject GetParserFor (SystemTransform st, Coordinate coord)
        {
            if (! ParserMap.ContainsKey(st))
                throw new NotImplementedException ("Unexpected file type value" + st);

            return ParserMap[st](coord);
        }
}

它更优雅一点,但这并不能解决问题。

理论上,我面临着一个有∞可能性的开关。可以说,无法手动填充并随着时间的推移对其进行维护。

第三个想法是制造工厂。但这无助于我根据来源系统和到达系统来定义使用哪个函数。

有解决这个问题的设计模式的想法吗?

【问题讨论】:

  • 似乎MyObject 非常易变,不适合struct
  • case 语句似乎完全忽略了switch 变量s。这真的有效吗?
  • 如果您有不同的转换并且必须手动编码,那么我没有得到“具有无限可能性的开关”声明,您仍然必须编写无限转换,那么重点在哪里?
  • ToSystemBToSystemX 等有哪些代码?如果它们真的完全不同,那么你的问题本质上很复杂。我能想到的唯一方法是将逻辑编码到数据文件中(或者不编码,但实际上将其写入您将发明的 DSL 中)。这样,C# 代码只需要找到一个正确的文件,读取它并运行它。文件名可以以initial 系统开头,以terminal 系统结尾。
  • 不完全清楚你最终想要达到的目标,但你可能想看看TypeConverter。如果您尝试自动计算是否存在通过 C1、C2、..CN 类型将 A 转换为 B 的路径,那么您应该阅读图上的路径查找。您还可以查看“计量单位”转换的实现...

标签: c# generics enums coordinate-transformation


【解决方案1】:

我不确定我是否完全理解您的要求,但对我来说,这听起来像一个简单的抽象就足够了。

    public abstract class CoordinateSystem
    {
        public double X {get; private set; }
        public double Y {get; private set; }
        public double Z {get; private set; }

        protected CoordinateSystem( Double x, Double y, Double z )
        {
            X = x;
            Y = y;
            Z = z;
        }

        public abstract CoordinateSystemA ToA();
        public abstract CoordinateSystemB ToB();
        public abstract CoordinateSystemC ToC();
    }

    public class CoordinateSystemA : CoordinateSystem
    {
        public CoordinateSystemA( Double x, Double y, Double z )
          : base(x, y, z )
        {}

        public override CoordinateSystemA ToA() {
            return new CoordinateSystemA(X,Y,Z);
        }
        public override CoordinateSystemB ToB() {
            return new CoordinateSystemB(2,2,2);
        }
        public override CoordinateSystemC ToC() {
            return new CoordinateSystemC(3,3,3);
        }
    }

    public class CoordinateSystemB : CoordinateSystem
    {
        public CoordinateSystemB( Double x, Double y, Double z )
          : base(x, y, z )
        {}

        public override CoordinateSystemA ToA() {
            return new CoordinateSystemA(1,1,1);
        }
        public override CoordinateSystemB ToB() {
            return new CoordinateSystemB(X,Y,Z);
        }
        public override CoordinateSystemC ToC() {
            return new CoordinateSystemC(3,3,3);
        }
    }

    public class CoordinateSystemC : CoordinateSystem
    {
        public CoordinateSystemC( Double x, Double y, Double z )
          : base(x, y, z )
        {}

        public override CoordinateSystemA ToA() {
            return new CoordinateSystemA(1,1,1);
        }
        public override CoordinateSystemB ToB() {
            return new CoordinateSystemB(2,2,2);
        }
        public override CoordinateSystemC ToC() {
            return new CoordinateSystemC(X,Y,Z);
        }
    }


    public static class Program {
        public static void Main() {
            CoordinateSystem coordinateSystem = new CoordinateSystemA(1,1,1);
            CoordinateSystem b = coordinateSystem.ToB();
            CoordinateSystem c = b.ToC();
        }
    }

通过将ToX(); 放在抽象类上,它会强制所有继承坐标系(类)来实现转换。然后,您可以在不了解系统的情况下在整个系统中使用抽象类,并轻松转换为您想要的系统。

如果这不能满足您的要求,我认为您应该看看访客模式。它有点复杂,但有很多例子。

【讨论】:

    猜你喜欢
    • 2022-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-10-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多