【问题标题】:Is it possible to change an object type in C#是否可以在 C# 中更改对象类型
【发布时间】:2014-05-31 10:52:12
【问题描述】:

为了满足客户的要求,我遇到了一些问题。

我会尽量保持示例非常简单,只是给出问题的想法,并希望提出解决方案。

此时我们有一个类“Shape”,形状有一些特化,可以是正方形、三角形等。

到目前为止一切顺利,一切正常。

现在出于某种原因,我的客户想要将系统中已经存在的正方形更改为三角形,但保留所有数据的形状。

这可能吗?有什么解决方法吗?

【问题讨论】:

  • 也许您只是将数据集处理为用户设置的新类的实例。
  • 我认为您应该为:Circle、Triangle 和 Cube 定义单独的类,并使用一些通用属性,这样您就可以轻松创建新形状并填充通用属性。
  • 你能给我们展示形状/正方形用法的例子吗?以便我们提出模式创意?
  • 不,您不能在创建对象后更改它的类型。您可以创建具有相似属性的新对象。
  • 我会创建一个辅助方法,它只从正方形实例中获取您需要的所有道具,并在三角形的构造函数中使用它

标签: c# .net object types


【解决方案1】:

您可以在数据类型之间进行转换。结果可以放置在一个新对象中。原始对象的类型不会改变。但通常你只在有意义的情况下为此提供一种机制。在将正方形转换为三角形的情况下,我看不出这有什么意义,但也许有一些关于您的特定应用程序的东西是有意义的(即,将正方形转换为具有相同周长或面积的三角形)。

有关不同类型之间转换的示例,请参阅 MSDN 上的 Using Conversion Operators

来自 MSDN 上的Casting and Type Conversions

因为 C# 在编译时是静态类型的,所以在变量被 声明,它不能再次声明或用于存储值 另一种类型,除非该类型可转换为变量的类型。 例如,没有从整数到任意任意值的转换 细绳。因此,在将 i 声明为整数后,您不能 为其分配字符串“Hello”,如下代码所示。

【讨论】:

  • 这将创建一个新对象,并且不会更改原始对象的类型。这给您留下了将所有对原始对象的引用替换为对新对象的引用的问题。
  • @DanielBrückner 是的,我想我在回答中说得很清楚,它不会改变类型。无论如何,如果您正在制作一个新对象,那么显然所有引用都指向旧对象。您不必在我的回答中指出这一点,因为在 cmets 中已经在问题和您自己的回答中指出了这一点。
【解决方案2】:

如果不创建所需类型的新对象并手动更新对它的 all 引用(这很容易出错),就不可能替换现有对象的类型 - 这我会考虑一种解决方法。

在设计方面,如果“类型”(在行为意义上,而不是静态类型系统中的实际类型)需要灵活,则可以通过委托来解决问题。对象本身将保持不变,但会交换一个委托。

【讨论】:

    【解决方案3】:

    您不能更改对象的运行时类型。只需创建一个新三角形,复制所有相关值并丢弃正方形。如果正方形已经被许多其他对象引用,这当然会变得很棘手,因为您必须更新所有引用。

    如果替换对象不是一种选择,您可能必须想出一个可以充当任何形状的通用形状类。例如,此类可以是一个围绕具体形状类的实例的薄包装器。这使您可以只用一个新的三角形替换被包裹的正方形,而外部世界可以保留对包裹类的所有引用。

    【讨论】:

    • 这归结为:使用组合而不是继承。包装类将由一个形状组成,该形状可以在运行时设置和更新,改变包装类的角色。组合 = 灵活性。
    • 兰普森定律的另一种应用
    【解决方案4】:

    Shape 类中定义并实现一个虚拟的Clone() 方法。你有两个“合理”的选择。无论您选择哪种实现方式,您都不能只是“发明”不存在的数据 - 正方形的边长为一侧,三角形的边长为 3。

    第一个选项是手动复制周围的所有字段:

    class Shape{
    
      public virtual Shape Clone(Shape target = null){
        if (target == null) target = new Shape();
        target.Prop1 = this.Prop1;
        return target;
      } 
    }
    
    class Square{
      public override Shape Clone(Shape target = null){
        if (target == null) target = new Square();
        base.Clone(target);
        if (target.GetType() == typeof(Square)){
          target.PropSquare1 = this.PropSquare1; // some casting etc
        }
      } 
    }
    

    // 改变类型: 变种三角形 = 新三角形(); square.克隆(三角形);

    第二个选项,为了方便起见,我更喜欢性能交易。它是使用序列化器将形状序列化为一种并反序列化为另一种。您可能需要处理中间的序列化结果。下面是伪代码版本:

    class Shape{
      public virtual T Clone<T>() where T: Shape{
        var data = JsonConvert.Serialize(this);
        data = data.Replace("Square","Triangle");
        return JsonConvert.Deserialize<T>(data);
      }
    }
    

    【讨论】:

    • Square.Clone() 未设置 Prop1
    • 好了,现在它调用 base.Clone
    • 您的代码如何从三角形转换为正方形?您的代码复制,但不转换
    • 三角形也不必有三个单独的边长。我怀疑这个系统可以通过不存储边长或顶点坐标,而是存储诸如旋转、平移和缩放之类的转换而变得相当远。这种转换也可以应用于不同的模板形状。
    【解决方案5】:

    有一个 Convert.ChangeType(dObject, typeof(SomeClass));

    但要使转换成功,value 必须实现 IConvertible 接口,因为该方法只是包装了对适当 IConvertible 方法的调用。该方法要求支持将 value 转换为 conversionType。

    【讨论】:

    • 这仅适用于 IConvertable,几乎不是复杂类的最佳接口
    • thats correct and I wouldnt 将它用于复杂的类.. 只是想说明有这样的方法但仅限于 IConvertable 接口(解决方法:将其限制为 IConvertable)
    【解决方案6】:

    你需要的是继承。

    这是一个模型:

    创建一个 StrokeStyle 类

    • 宽度
    • 颜色
    • 类型(虚线、实线)

    创建一个 FillStyle 类

    • 颜色
    • 类型(实心、渐变)

    创建一个具有 strokeStyle 和 fillStyle 属性的 VectorShape 类(每个都是类的一个实例)。

    创建 Square 和 Triangle 类,都继承了 VectorShape 类。它们将共享 VectorShape 属性。您必须用新的 Triangle 实例替换您的 square 实例并复制您要保留的属性。

    您还可以使用 shapetype 属性创建一个单一的类,该属性将是“方形”或“三角形”……然后您可以在不替换对象的情况下更改类型。但是您必须在所有方法中处理 shapetype,即:computeArea()。这将导致代码难以管理。这是可能的,但这是不好的方式。

    【讨论】:

    • 如果 Square 或 Triangle 拥有它的样式而不是继承它,这会更好。然后可以将样式对象传递给新形状,而无需复制。
    【解决方案7】:

    您无法更改类型,但可以通过适当的设计解决此问题。 如果想法是每个对象都是一个 Shape 并且它具有必须是可替换的附加信息,那么将它作为一个单独的成员保存是有意义的。例如(伪):

    public class ShapeContainer 
    {
     public int x { get; set; }
     public int y { get; set; }
    
     public ISpecificShape SpecificShape { get; set; }
    }
    
    public class Triangle : ISpecificShape 
    {
    // ...
    // ...
    }
    
    public class Rectange : ISpecificShape 
    {
    // ...
    // ...
    }
    

    这样,你可以改变具体的形状。

    如果您希望它被输入,您可以将以下通用 Get 函数添加到 Shape:

    GetSpecificShape<T>() where T : ISpecificShape 
    {
     return (T)this.SpecificShape;
    }
    

    如果数据类型不匹配但符合您的设计要求,则会引发异常。

    你怎么看?

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2016-04-24
      • 1970-01-01
      • 1970-01-01
      • 2018-02-20
      • 1970-01-01
      • 2014-03-17
      • 2016-04-15
      相关资源
      最近更新 更多