将子类转换为另一个具有基类的子类

cast child class to another with base class

提问人:UK Rock 提问时间:12/20/2022 更新时间:12/20/2022 访问量:69

问:

我需要一些长度单位可以将它们转换在一起

长度类作为父级 米、厘米、毫米作为孩子:

public abstract class Length
{
}

public class Meter: Length
{
   public Meter(double val)
   {
       Value = val;
   }

   public double Value { get; set; }

   public static explicit operator Centimeter(Meter m)
   {
        return new Centimeter(m.Value * 100);
   }

   public static explicit operator Millimeter(Meter m)
   {
        return new Millimeter(m.Value * 1000);
   }
}

public class Centimeter: Length
{

   public Centimeter(double val)
   {
       Value = val;
   }

   public double Value { get; set; }

   public static explicit operator Meter(Centimeter cm)
   {
        return new Meter(cm.Value / 100);
   }

   public static explicit operator Millimeter(Centimeter cm)
   {
        return new Millimeter(cm.Value * 10);
   }
}

public class Millimeter: Length
{

   public Millimeter(double val)
   {
       Value = val;
   }

   public double Value { get; set; }

   public static explicit operator Meter(Millimeter mm)
   {
        return new Meter(mm.Value / 1000);
   }

   public static explicit operator Centimeter(Millimeter mm)
   {
        return new Centimeter(mm.Value / 10);
   }
}

我可以用这个代码将米转换为毫米:

Meter m = new Meter(3)
Millimeter mm = (Millimeter)m; //it's ok. result is 3000

但是我需要有基类类型来保存我的变量:

Length l;
if (true)
   l = new Meter(3);
else
   l = new Centimeter(20)
Millimeter m = (Millimeter)l;//runtime Error

我收到运行时错误: System.InvalidCastException:“无法将”米“类型的对象强制转换为”毫米“类型。

C# 继承 强制转换

评论

1赞 dan1st is crying 12/20/2022
您是否考虑过常规的非静态方法,例如定义(抽象)?toMillimeter()Length
1赞 DavidG 12/20/2022
A 不是,所以即使在概念上,这样做也没有意义。你为什么要这样做呢?只需使用MeterMillimeterLength
2赞 DavidG 12/20/2022
此外,我什至不会在这里打扰多个派生类,我会坚持使用并使用一些静态方法来创建它,例如将所有单位转换为一些通用的基本单位(如毫米)LengthLength.FromMeters(5)

答:

0赞 DRapp 12/20/2022 #1

这可能是你希望得到的方向。让一个类能够整体处理毫米、厘米或米的转换。在本类示例中,我让类将值存储到最低粒度的毫米。现在,也就是说,你甚至可能希望毫米级别降低到整数与双倍,除非你想允许毫米的分数,但你可以选择。

public class MyLength
{
    // always store at the lowest possible scale here
    private double _millsValue;
    private MyLength( double alwaysMillimeters)
    {
        _millsValue = alwaysMillimeters;
    }

    // have constructor return a MyLength based on whatever type wanted with
    // the STATIC method name make sure it builds out to proper millimeter reference
    public static MyLength Millimeter( double val)
    { return new MyLength(val); }

    // 10 millimeters to 1 centimeter, so an incoming 5 centimeters = 50 millimeters
    public static MyLength Centimeter( double val)
    { return new MyLength(val * 10); }

    // similarly, 1000 millimeters to 1 meter, so 2 meters = 2000 millimeters
    public static MyLength Meter (double val)
    { return new MyLength(val * 1000); }

    // Now, expose public getter / setter that are bound to the
    // underlying _millsValue and divide back out by 10 or 1000 respectively
    public double Millimeters
    {
        get { return _millsValue; }
        set { _millsValue = value; }
    }

    public double Centimeters
    {
        get { return _millsValue / 10; }
        set { _millsValue = value * 10; }
    }

    public double Meters
    {
        get { return _millsValue / 1000; }
        set { _millsValue = value * 1000; }
    }
}

并展示如何应用和获取值,无论创建的基础如何,并通过 getters / setter 为您处理转换

public class LengthTest
{
    public LengthTest()
    {
        var L = MyLength.Millimeter(1000);
        var mm = L.Millimeters;     // returns 1000
        var cm = L.Centimeters;     // returns 100
        var m = L.Meters;           // returns 1

        var C = MyLength.Centimeter(3);
        mm = C.Millimeters;     // returns 30
        cm = C.Centimeters;     // returns 3
        m = C.Meters;           // returns .03

        var M = MyLength.Meter(2);
        mm = M.Millimeters;     // returns 2000
        cm = M.Centimeters;     // returns 200
        m = M.Meters;           // returns 2


        // can also SET values based on respective named getter/setter
        var someVal = MyLength.Centimeter(0);
        someVal.Centimeters = 250;
        mm = someVal.Millimeters;     // returns 2500
        cm = someVal.Centimeters;     // returns 250
        m = someVal.Meters;           // returns 2.5

        someVal.Meters = .75;
        mm = someVal.Millimeters;     // returns 750
        cm = someVal.Centimeters;     // returns 75
        m = someVal.Meters;           // returns .75

        someVal.Millimeters = 250;
        mm = someVal.Millimeters;     // returns 250
        cm = someVal.Centimeters;     // returns 25
        m = someVal.Meters;           // returns .25


    }
}

评论

0赞 UK Rock 12/22/2022
感谢您的回答,您在此示例中是正确的,但我需要使用项目中的所有单位,例如位置转换器,将十进制值转换为度/分、分钟、UTM、MGRS 等。在这种情况下,每个单元都有特殊的变量、方法......我需要使用 cast 将它们转换在一起。
0赞 DRapp 12/22/2022
@UKRock,这就是这门课的重点。保存到一个共同的基础。然后,公开从任何基础转换为最终的方法。在这里,我可以通过提供毫米、厘米或米来创造,但作为回报,可以从任何一个获得。如果根据纬度/经度坐标与时间的相似输入提供位置对象,则存储在一个公共对象中,并通过其各自的公式获取另一个对象。你有没有尝试过这个代码示例?
0赞 UK Rock 12/24/2022
因此,如果我想获得度/分钟,我必须每次都将度转换为度/分钟。或者,如果我只想使用 UTM,我必须将 if 转换为度数作为基本单位。这对性能太差了
0赞 DRapp 12/24/2022
@UKRock,那么我会存储底层基础 AS 度/分钟。你能编辑你的帖子并显示你正在为你的UTM / Degree/Min上下文处理什么吗?我也会尝试编写一个类似的类来为您处理这个问题。