【问题标题】:Call Constructor Base after Code Execution代码执行后调用构造函数基
【发布时间】:2012-05-26 05:44:46
【问题描述】:

假设我们有 A 类和 B 类。ClassB 扩展了 A 类。(ClassB : ClassA)

现在假设每当我实例化 ClassB 时,我想运行一些随机代码,然后才调用“base”以到达 ClassA 构造函数。

喜欢:

class ClassA
{
    public ClassA()
    {
        Console.WriteLine("Initialization");
    }  
}

class ClassB : ClassA
{
    public ClassB() //: base() 
    {
        // Using :base() as commented above, I would execute ClassA ctor before                                                         //          Console.WriteLine as it is below this line... 
        Console.WriteLine("Before new");
        //base() //Calls ClassA constructor using inheritance
        //Run some more Codes here...
    }
}

在我通常使用的编程语言中,我可以做到这一点,只需在Console.WriteLine() 之后调用super();但我不能在 C# 中实现它。有没有其他语法或其他方法可以做到这一点?

【问题讨论】:

  • 在 C# 中使用基本构造函数无法做到这一点。您可以在基本方法中提取登录并使用 base.Method() 调用它。
  • 您通常使用哪种语言?因为如果它是 Java,那你就错了——你也不能那样做......
  • @JonSkeet 我知道这已经很老了,但听起来像 Python。

标签: c# constructor


【解决方案1】:

有一种使用实例变量初始化器的 hacky 方法:

using System;

class ClassA
{
    public ClassA()
    {
        Console.WriteLine("Initialization");
    }  
}

class ClassB : ClassA
{
    private readonly int ignoreMe = BeforeBaseConstructorCall();

    public ClassB()
    {
    }

    private static int BeforeBaseConstructorCall()
    {
        Console.WriteLine("Before new");
        return 0; // We really don't care
    }
}

class Test
{
    static void Main()
    {
        new ClassB();
    }    
}

hacky 的做法是重新考虑如何构建ClassB 开始。与其让客户端直接调用构造函数,不如为它们提供一个静态方法来调用:

public static ClassB CreateInstance()
{
    Console.WriteLine("Before initialization stuff");
    return new ClassB();
}

【讨论】:

  • 有一种非 hacky,或者至少不那么 hacky 的方式来使用抽象类来做到这一点。参考下面
【解决方案2】:

我很惊讶没有人建议使用抽象方法——尽管它确实依赖于被实现为抽象类的基础,它不适用于所有情况(尽管你可以简单地分叉继承并将非抽象堆叠在顶部,如果你必须)。这样做的好处是可以确保代码的完整性,而无需求助于黑客行为。因为我们使用的是抽象,所以不声明 initCode 就无法实例化派生类。

using System;

abstract class ClassA
{
    internal abstract initCode();
    public ClassA()
    {
        initCode();
        Console.WriteLine("Initialization");
    }  
}

class ClassB : ClassA
{
    public ClassB()
    {
    }
    
    internal override initCode()
    {
        Console.WriteLine("Before new");
        return 0; // We really don't care
    }
}

//If you need to effectively get the non-abstract of ClassA
class ClassC : ClassA
{
    public ClassB()
    {
    }
    
    internal override initCode()
    {
    }
}

【讨论】:

    【解决方案3】:

    如果您可以摆脱调用静态方法的另一个技巧。

    public class ClassA
    {
        public ClassA()
        {
            Debug.WriteLine("Call A Constructor");
        }
    }
    
    public class ClassB:ClassA
    {
        public ClassB():this(aMethod())
        {
        }
    
        private ClassB(object empty):base()
        {
            Debug.WriteLine("Class B Second Constructor");
        }
    
        private static object aMethod()
        {
            Debug.WriteLine("Run me First");
            return null;
        }
    }
    

    【讨论】:

      【解决方案4】:

      其实你可以:

      class Foo
      {
          public Foo(string s)
          {
              Console.WriteLine("inside foo");
              Console.WriteLine("foo" + s);
          }
      }
      
      class Bar : Foo
      {
          public Bar(string s) : base(((Func<string>)(delegate ()
          {
              Console.WriteLine("before foo");
              return "bar" + s;
          }))())
          {
              Console.WriteLine("inside bar");
          }
      }
      
      class Program
      {
          static void Main(string[] args)
          {
              new Bar("baz");
          }
      }
      

      输出:

      before foo
      inside foo
      foobarbaz
      inside bar
      

      但如果可能的话,我宁愿不使用这个技巧。

      【讨论】:

        【解决方案5】:

        另一个优雅的解决方案是彻底重新思考对象的构造方式。在基类的构造函数中,您可以调用自己的 construct 函数,并省略依赖的未来构造函数,方法如下:

        public class ClassA
        {
            public ClassA()
            {
                Construct();
            }
        
            public virtual void Construct()
            {
                Console.WriteLine("3");
            }
        }
        
        public class ClassB : ClassA
        {
            public override void Construct()
            {
                Console.WriteLine("2");
                base.Construct();
            }
        }
        
        public class ClassC : ClassB
        {
            public override void Construct()
            {
                Console.WriteLine("1");
                base.Construct();
            }
        }
        

        【讨论】:

        • 这种方法的问题是继承者可以覆盖基类的初始化方式(如果有的话)。这是有问题的,因为一个类应该总是能够保护它自己的不变量,尤其是如果这个类被设计成可扩展的。
        【解决方案6】:

        C# 无法做到这一点。最好的办法是将该代码提取到父级中它自己的方法中,然后在准备好时从子级调用它。

        【讨论】:

          【解决方案7】:

          C# 不允许在构造函数体内调用基本构造函数,这与 Java 不同。

          【讨论】:

            【解决方案8】:

            您不能调用基本构造函数。 但是不同的是,当您声明派生类的对象时,会调用派生的构造函数和基类。

                class ClassA
            {
                public ClassA()
                {
                    Console.WriteLine("Initialization");
                }  
            }
            
            class ClassB : ClassA
            {
                public ClassB() //: base() 
                {
                    // Using :base() as commented above, I would execute ClassA ctor before                                                         //          Console.WriteLine as it is below this line... 
                    Console.WriteLine("Before new");
                    //base() //Calls ClassA constructor using inheritance
                    //Run some more Codes here...
                }
            }
            void main(string[] args)
                {
                  ClassB b = new ClassB();
            
                }
            

            【讨论】:

              【解决方案9】:

              最近我遇到了一个场景,我需要在将结果传递到 base 之前计算一些逻辑。

              我可以做类似的事情

              public SomeConstructor: base(FlagValue == FlagValues.One || FlagValues.Two ? "OptionA" : "OptionB")
              {
              
              }
              

              但我觉得这很难看,而且横向会变得很长。所以我选择使用 Func Anonymous 方法。

              例如假设你有一个基类,

              public class SomeBaseClass
              {
                public SomeBaseClass(Func<string> GetSqlQueryText){
                  string sqlQueryText = GetSqlQueryText();
                  //Initialize(sqlQueryText);
                }
              }
              

              现在你继承了它并想要做一些逻辑来确定 sql 查询文本,

              public class SomeSqlObject : SomeBaseClass
              {
                public SomeSqlObject(ArchiveTypeValues archiveType)
                      : base(delegate()
                      {
                          switch (archiveType)
                          {
                              case ArchiveTypeValues.CurrentIssues:
                              case ArchiveTypeValues.Archived:
                                  return Queries.ProductQueries.ProductQueryActive;
                              case ArchiveTypeValues.AllIssues:
                                  return     string.Format(Queries.ProductQueries.ProductQueryActiveOther, (int)archiveType);
                              default:
                                  throw new InvalidOperationException("Unknown archiveType");
                          };
                      })
                  {
                      //Derived Constructor code here!
                  }
              
              }
              

              通过这种方式,您可以在调用 Base 之前执行代码,并且(在我看来)它并不是真正的 hacky。

              【讨论】:

              • 认为您可以访问基类以使参数成为 func 真的很牵强。
              • 继承基类并重载构造函数。然后继承它。
              【解决方案10】:

              我遇到了同样的问题。如果您无权访问基类,我发现此解决方案是最好的。

              public class BaseClass
              {
                  public BaseClass(string someValue)
                  {
                      Console.WriteLine(someValue);
                  }
              }
              
              public class MyClass : BaseClass
              {
                  private MyClass(string someValue)
                      : base(someValue)
                  {
                  }
              
                  public static MyClass GetNewInstance(string someValue, bool overrideValue = false)
                  {
                      if (overrideValue)
                      {
                          someValue = "42";
                      }
                      return new MyClass(someValue);
                  }
              }
              

              【讨论】:

                猜你喜欢
                • 1970-01-01
                • 2013-06-22
                • 1970-01-01
                • 2015-08-18
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2023-03-30
                • 2013-07-21
                相关资源
                最近更新 更多