【问题标题】:Initialize base class in .NET在 .NET 中初始化基类
【发布时间】:2010-10-04 07:40:55
【问题描述】:

如果我需要用现有对象初始化对象的基础,我该怎么办?例如,在这种情况下:

public class A
{
    public string field1;
    public string field2;
}

public class B : A
{
    public string field3;
    public void Assign(A source)
    {
        this.base = source; // <-- will not work, what can I do here?
    }
}

Assign() 方法显然可以逐个字段地为基类赋值,但是没有更好的解决方案吗?由于类 B 继承自 A,因此必须有一种方法可以将 A 分配给 B.base

在 C++ 中这将是一件微不足道的事情,但我似乎无法掌握如何在 .NET 中做到这一点

【问题讨论】:

    标签: c# .net oop


    【解决方案1】:

    不,您尝试的语法是不可能的。在 C# .NET 中,您需要这样做:

    public void Assign(A source) {
        field1 = source.field1;
        field2 = source.field2; 
    }
    

    【讨论】:

      【解决方案2】:

      很遗憾,base 是只读的。

      [编辑]
      好吧,也许不是那么不幸。基类和子类之间的关系是IS-A 而不是HAS-A。通过允许子类更改基类的实例,您允许子类更改其自己的引用,因为它是IS-A 基类。如果您确实需要此功能,那么我建议您更改继承模型以反映您真正想做的事情。

      类似这样的:

      public class A
      {
          public string field1;
          public string field2;
      }
      
      public class B
      {
          public string field3;
          public A a;
      
          public void Assign(A source)
          {
              this.a = source;
          }
      }
      

      似乎更合适,含义和功能更清晰。

      【讨论】:

      • +1 这确实有用于组合的继承的味道。
      • 我不想这么说,但看起来这种方法是我唯一可以使用的方法:(
      • 这是一种迂回的方式。我强烈建议使用构造函数来设置这些字段。如果一个变量对一个类来说是私有的,那是有原因的。如果您有希望孩子操作的父类,请将成员设为受保护或公开。
      【解决方案3】:
              public Assign(A a)
              {
                  foreach (var prop in typeof(A).GetProperties())
                  {
                      this.GetType().GetProperty(prop.Name).SetValue(this, prop.GetValue(a, null),null);
                  }
              }
      

      基本上,它使用反射来获取 base 的所有属性并将 this 的值分配给 A 中存在的所有值。

      编辑:对于所有反对者,我现在使用具有 100 个整数变量的基类快速测试了这一点。然后我在子类中有这个分配方法。运行时间为 46 毫秒。我不了解你,但我完全可以接受。

      【讨论】:

      • 这在技术上可能有效,但这很丑陋并且会减慢对象的初始化速度。
      • 我讨厌人们说反射很慢。除非您正在处理性能关键代码,否则这真的不是问题。我已经完成了反映许多类及其所有属性(在具有 100 个表的整个 DataContext 类)上的反射,并且完全没有花费时间。
      • 呕吐!最好建议个人不要做那样的事情或使用组合,而不是向他们展示如何滥用反射......
      • 我明白反射不是为这种访问而进行的;我也了解我们获得了工具和 API 以便使用它们,因此您的解决方案似乎很好。我没有将其标记为答案,因为这不是我要问的,但我赞成负责任地使用这种方法
      • 我猜这就是 MemberwiseClone 所做的。它可能很有用,但我也不喜欢反思。
      【解决方案4】:

      根据 MSDN,“base”只能用于以下操作:

      • 调用基类上已被另一个方法覆盖的方法。
      • 指定在创建派生类的实例时应调用哪个基类构造函数。

      【讨论】:

        【解决方案5】:

        为什么需要?通过声明一个新的 B,CLR 自动调用这两个类的构造函数。

        B myB = new B();
        

        B new 具有两个类的字段。但是,除非您喜欢 null,否则您应该使用初始化程序声明它们:

        public string field1 = "";
        public string field2 = string.Empty;
        

        【讨论】:

          【解决方案6】:

          虽然这里有很多很好的答案,但我认为正确的方法是链接构造函数:

          public class A
          {
              public string field1;
              public string field2;
          
              public A(string field1, string2 field2)
              {
                   this.field1 = field1;
                   this.field2 = field2;
              }
          }
          
          public class B : A
          {
              public string field3;
          
              public B(string field1, string2 field2, string field3)
                  : base(field1, field2)
              {
                  this.field3 = field3;
              }
          }
          

          【讨论】:

            【解决方案7】:

            我希望我不是唯一一个认为换掉基类是一种糟糕的设计模式的人。另一种方法是用组合代替继承:

            public class A
            {
                public string Field1 { get; set; }
                public string Field2 { get; set; }
            }
            
            public class B
            {
                public A A { get; set; }
                public string Field3 { get; set; }
            
                public B(A a) { this.A = a; }
            }
            

            现在写这样的东西很简单:

            B b = new B ( new A { Field1 = "hello", Field2 = "world" } );
            
            b.A = new A { Field1 = "hola", Field2 = "luna" };
            

            【讨论】:

            • 我不认为 OO 是一个糟糕的设计模式,你的例子让 OO 完全没用。
            • @Nick:在很多情况下,组合解决了继承无法解决的问题。经典的教科书示例是当您需要将一个类中的实现与另一个类进行热交换时,这正是作者想要做的。 (续...)
            • (来自上一个) 由于基类是对对象实例的引用,而不仅仅是一个花哨的容器,因此不可能将一个基类交换为另一个基类。因此,作者问题的唯一合理解决方案是将“基础”包装在属性中。为什么你认为我的方法是错误的?
            【解决方案8】:

            错误的问题。 你显然在这里滥用继承权。 尝试重构它,以便保留对 A 的引用作为成员字段。 如果您需要多态性,请考虑使用通用基类或更好的接口 - 一个接口。

            【讨论】:

              【解决方案9】:

              这些字段的意图是在对象构造期间初始化一次,还是可以在对象的生命周期内多次调用“Assign”?如果是后者,您可以忽略其余部分:)

              Andrew 对 IS-A 和 HAS-A 的区分很重要;如果关系真的是HAS-A,他的组合解决方案就是要走的路。

              如果 IS-A 关系更有意义(并且您可以修改 A),则复制构造函数可能是个好主意:

              public class A
              {
                  public string field1;
                  public string field2;
              
                  public A(A copyFrom)
                  {
                      this.field1 = copyFrom.field1;
                      this.field2 = copyFrom.field2;
                  }
              }
              
              public class B : A
              {
                  public string field3;
              
                  public B(A source)
                      : base(source)
                  {
                  }
              }
              

              您最终不得不复制 A 的每个属性,但这样做的责任在于它所属的 A。

              【讨论】:

                【解决方案10】:
                    [TestMethod]
                    public void TestMethod()
                    {
                        A a = new A();
                        a.field1 = "test";
                        string xml = Serialize(a);
                        xml = xml.Replace("A", "B");
                        B b = Deserialize(xml);
                
                        Assert.AreEqual("test", b.field1);
                    }
                
                    public string Serialize(A a)
                    {
                        System.IO.StreamReader streamReader = null;
                        System.IO.MemoryStream memoryStream = null;
                        try
                        {
                            memoryStream = new System.IO.MemoryStream();
                            XmlSerializer serializer = new XmlSerializer(typeof(A));
                            serializer.Serialize(memoryStream, a);
                            memoryStream.Seek(0, System.IO.SeekOrigin.Begin);
                            streamReader = new System.IO.StreamReader(memoryStream);
                            return streamReader.ReadToEnd();
                        }
                        finally
                        {
                            if ((streamReader != null))
                            {
                                streamReader.Dispose();
                            }
                            if ((memoryStream != null))
                            {
                                memoryStream.Dispose();
                            }
                        }
                    }
                
                    public static B Deserialize(string xml)
                    {
                        System.IO.StringReader stringReader = null;
                        try
                        {
                            stringReader = new System.IO.StringReader(xml);
                            XmlSerializer serializer = new XmlSerializer(typeof(B));
                            return ((B)(serializer.Deserialize(System.Xml.XmlReader.Create(stringReader))));
                        }
                        finally
                        {
                            if ((stringReader != null))
                            {
                                stringReader.Dispose();
                            }
                        }
                    }
                

                【讨论】:

                • 这如何回答这个问题?为什么序列化相关?
                猜你喜欢
                • 2021-03-23
                • 1970-01-01
                • 2019-02-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2010-11-10
                相关资源
                最近更新 更多