【问题标题】:Implicit conversations from child(derived) class to parent(base) class从子(派生)类到父(基)类的隐式对话
【发布时间】:2015-05-19 06:58:48
【问题描述】:

假设我有以下两个类

public class Animal
{
    public string name { get; set; }
}

public class Cat: Animal
{
   public int age { get; set; }
   public string type { get; set; }
}

现在我想通过隐式转换将我的派生类“Cat”匹配属性(例如名称)转换为我的基类“Animal”,如下所示。

            Cat cat = new Cat();
            cat.name = "diana";
            cat.age = 2;
            cat.type = "Siamese-American Shorthair";

            Animal animal = new Animal();

            animal = (Animal)cat;
             // or
            animal = cat as Animal;

因此,在进行上述编码时,它会正常工作,并且会在 Animal 类对象中隐含 name 属性,但是如果我检查 Animal 类的对象,它实际上包含 Cat 类的对象,它是 cat 而实际上没有得到对象动物类。

所以请帮助我克服这种情况,这样我就可以直接将我的子类属性隐式转换为具有正确父类对象的匹配类属性。

【问题讨论】:

  • 您的代码无法编译并且它没有显示您的实际问题。顺便说一句,你不需要新的动物或演员,Animal animal = cat; 也应该可以。
  • CatName 属性已经来自它的父级 Animal。如果您确实需要 Animal 类型,请创建一个 Animal 实例。
  • @YuvalItzchakov 你是对的,但我有我描述的场景以及我需要动物类型的任何方式。我希望有人能帮助我
  • 为什么你需要一只猫才能成为动物而不是猫?这听起来像是一个不错的设计错误。
  • @HimBromBeere 你可能是对的,它可能是设计模式错误,但现在我没有任何选择来修改这个设计模式

标签: c#


【解决方案1】:

猫永远是猫,即使你只是在看动物。您不能删除对象的实际类型;将其转换为父类型只会影响您将其存储到的变量,但底层对象保持不变。

Cat cat = new Cat(); // cat is a cat

Animal catAnimal = cat; // a cat is also an animal
Console.WriteLine(catAnimal.GetType()); // still a cat

Cat newCat = (Cat)catAnimal;

Console.WriteLine(newCat == cat); // the same cat
Console.WriteLine(animal == cat); // the same cat

【讨论】:

  • 你是对的,底层对象保持不变,但我的要求是如何将任何方式转换为父类型。
  • 您可以像我在回答中显示的那样做到这一点。把它当作动物来使用,而忽略它实际上是一只猫。
  • 无需将Cat 转换为Animal(您的第二行)。
【解决方案2】:

首先,这不是隐式转换,而是显式转换。 一个隐式转换的例子是这样的:

int x = 123;
string y = "asdf" + x; // this is the implicit conversion.

第二,你不能“解开”猫。即使您的参考是object 类型,它仍然是cat

第三,来自任何类的任何对象都将保留其父类的属性和字段,除非它们被声明为private,因此将猫转换为动物以获得其名称属性是多余的。

那么,这样的演员阵容有用吗?
答案是肯定的,它可能在以下情况下:

  • 您的派生类使用 new 关键字隐藏基类功能。
  • 您的派生类显式实现了interface,隐式实现与显式实现不同。

以下是这些情况的示例:

当派生类中的属性或方法声明为new时,将对象转换为其基类以使用属性或方法:

public class Base {

    internal virtual string X() {
        return "Base";
    }
}
public class Derived1 : Base
{
    internal new string X()
    {
        return "Derived 1";
    }
}

public class Derived2 : Base 
{
    internal override string X()
    {
        return "Derived 2";
    }
}

Derived1 a = new Derived1();
Base b = new Derived1();
Base c = new Derived2();
Console.WriteLine("Derived1 as Derived1: "+ a.X()); // Derived1 as Derived1: Derived 1
Console.WriteLine("Derived1 as Base: " + b.X()); // Derived1 as Base: Base
Console.WriteLine("Derived2 as Base: " + c.X()); // Derived2 as Base: Derived 2

See fiddle here

当类用隐式实现重载显式实现时,将对象转换为它实现的接口之一

public interface IBlabla {
    string bla();
}

public class BlaBla : IBlabla 
{
    public string bla() {
        return "implicit";
    }

    string IBlabla.bla()
    {
        return "EXPLICIT";
    }
}

BlaBla Myclass = new BlaBla();    
Console.WriteLine(Myclass.bla()); // implicit
Console.WriteLine(((IBlabla)Myclass).bla()); // EXPLICIT

See fiddle here

【讨论】:

    【解决方案3】:

    您不能更改对象类型。正如 Yuval Itzchakov 已经提到的,您需要创建一个 Animal 类型的实例。您只能为您的 Animal 类创建某种复制构造函数,将所有属性和变量从给定的 Animal 复制到新的:

    public Animal(Animal animal) {
        this.Name = animal.Name;
        // further variables and properties to reset the state exactly to the state of the given animal
    }
    

    现在您可以像这样从每个派生类型创建Animal 类型的实例:

    Animal ani = new Animal(cat);
    

    但对我来说,这听起来仍然像是一个设计缺陷(可能是XY-problem)。如果你需要访问你的猫的Name-property,你不需要强制转换为它的基本类型。

    【讨论】:

      【解决方案4】:

      如果您真的想要,您可以按照以下方式做一些事情

       internal class Program
      {
          public class Animal
          {
              public string Name { get; set; }           
          }
      
          public class Cat : Animal
          {
              public int Age { get; set; }
              public string Type { get; set; }
          }
      
          public static void Main()
          {
              var cat = new Cat();
              cat.Name = "Puss";
      
              var animal = cat.ToBaseClass<Animal, Cat>();
      
              Debug.Assert(!(animal is Cat));
              Debug.Assert(animal.Name == "Puss");
      
          }
      }
      
      public static class ReflectionHelper
      {
          public static TBase ToBaseClass<TBase, TDerived>(this TDerived from) 
              where TBase : new()
              where TDerived : TBase
          {
              var result = new TBase();
      
              foreach (PropertyDescriptor propertyDescriptor in TypeDescriptor.GetProperties(result))
              {
                  propertyDescriptor.SetValue(result, propertyDescriptor.GetValue(from));
              }
      
              return result;
          }
      }
      

      ...但它会很丑;)

      【讨论】:

      • 使用上述方法我已经尝试过,但问题是它会显式地从一个对象转换为另一个对象,因此它可能会有性能损失
      【解决方案5】:

      感谢所有帮助我解决问题的人,我非常认真地对待每个人的回答和问题。 我还找到了另一种可能对某人有用的解决方案。由于这个问题是关于从子类到父类的具有相同属性的转换,我们可以使用 AutoMapper,它可以对任何类从子类到父类或父类到子类进行转换。首先,您可以在给出的链接中从 nuget 下载或更新 AutoMapper,并将其作为参考添加到您的项目或应用程序中。

      https://www.nuget.org/packages/AutoMapper/

      现在根据我的问题,如果我想将属性从子类转换为父类,而不是使用 AutoMapper 功能进行编码,如下所示。

           Mapper.CreateMap<Cat, Animal>();
           Animal animal = Mapper.Map<Animal>(cat);
      

      我希望这个解决方案对你有用,因为它解决了我的问题。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-08-10
        • 2015-05-23
        • 1970-01-01
        • 2015-02-25
        • 2012-12-10
        相关资源
        最近更新 更多