【问题标题】:Call one constructor from another从另一个调用一个构造函数
【发布时间】:2020-03-14 08:58:38
【问题描述】:

我有两个将值提供给只读字段的构造函数。

public class Sample
{
    public Sample(string theIntAsString)
    {
        int i = int.Parse(theIntAsString);
        _intField = i;
    }

    public Sample(int theInt) => _intField = theInt;
    public int IntProperty    => _intField;

    private readonly int _intField;
}

一个构造函数直接接收值,另一个做一些计算得到值,然后设置字段。

现在是关键:

  1. 我不想复制 设置代码。在这种情况下,只有一个 字段已设置,但当然可能有 不止一个。
  2. 要使字段只读,我需要 从构造函数中设置它们,所以 我无法将共享代码“提取”到 实用函数。
  3. 我不知道怎么称呼 另一个构造函数。

有什么想法吗?

【问题讨论】:

    标签: c# constructor


    【解决方案1】:

    像这样:

    public Sample(string str) : this(int.Parse(str)) { }
    

    【讨论】:

    • @Avi: 你可以创建一个static 方法来操作参数。
    • 可以知道这个的执行顺序吗? Sample(string) 中的所有内容都将首先执行然后Sample(int) 或 int 版本将首先执行然后它会回到字符串版本? (就像在 Java 中调用 super() 一样?)
    • @RosdiKasim:基类构造函数总是首先运行。 this 在其基类被初始化之前,您无法使用或查看它。
    • @ivan_pozdeev:是的,你可以;使用?: 或调用静态方法。
    • @GeorgeBirbilis:是的。他想在调用另一个 ctor 之前运行代码(在参数上)。那时,还没有实例。
    【解决方案2】:

    如果没有在自己的方法中进行初始化就无法令人满意地实现您想要的(例如,因为您想在初始化代码之前做太多事情,或者将其包装在 try-finally 中,或其他任何东西),您可以拥有任何或所有构造函数都通过引用初始化例程来传递只读变量,然后该例程将能够随意操作它们。

    public class Sample
    {
        private readonly int _intField;
        public int IntProperty => _intField; 
    
        private void setupStuff(ref int intField, int newValue) => intField = newValue;
    
        public Sample(string theIntAsString)
        {
            int i = int.Parse(theIntAsString);
            setupStuff(ref _intField,i);
        }
    
        public Sample(int theInt) => setupStuff(ref _intField, theInt);
    }
    

    【讨论】:

    • +1 真正的解决方案。使用base(...)this(...) 我们只能执行非常有限的操作。
    • 完全同意 - 其他解决方案有效,但不是正确的方法 (TM)!
    • out关键字代替ref怎么样?
    • @nawfal:因为如果变量是readonly就不行。
    • @JohnCarpenter:如果只需要设置一个readonly字段,设置它的代码可以调用方法并使用返回值分配字段,但可以直接写入任意数量的字段与ref。此外,如果重要,通过ref 参数所做的更改会立即发生,甚至在函数返回之前,而使用函数返回值所做的更改则不会。
    【解决方案3】:

    在构造函数的主体之前,使用:

    : base (parameters)
    
    : this (parameters)
    

    例子:

    public class People: User
    {
       public People (int EmpID) : base (EmpID)
       {
          // Add more statements here.
       }
    }
    

    【讨论】:

    • 不幸的是,如果我需要对构造函数调用之间的参数进行一些操作,则不起作用。
    • @Denis 你不能在中间链接一个构造函数来达到同样的效果吗?
    • @Denis 在调用构造函数之前你不能做任何事情。如果您想在初始化对象的属性之前做一些事情,请在构造函数示例init() 之外的方法中移动初始化。您可以从任何构造函数中调用此方法。
    • @AbdullahShoaib 当您需要调用父构造函数时不需要。
    【解决方案4】:

    我正在改进 supercat 的答案。我想也可以做到以下几点:

    class Sample
    {
        private readonly int _intField;
        public int IntProperty
        {
            get { return _intField; }
        }
    
        void setupStuff(ref int intField, int newValue)
        {
            //Do some stuff here based upon the necessary initialized variables.
            intField = newValue;
        }
    
        public Sample(string theIntAsString, bool? doStuff = true)
        {
            //Initialization of some necessary variables.
            //==========================================
            int i = int.Parse(theIntAsString);
            // ................
            // .......................
            //==========================================
    
            if (!doStuff.HasValue || doStuff.Value == true)
               setupStuff(ref _intField,i);
        }
    
        public Sample(int theInt): this(theInt, false) //"false" param to avoid setupStuff() being called two times
        {
            setupStuff(ref _intField, theInt);
        }
    }
    

    【讨论】:

    • 这可能允许第三方通过调用new Sample(str, false)来创建样本而不进行设置。
    • 这不会编译。
    • 这不是好方法;令人困惑;不必要的复杂。如果您使用this 调用另一个构造函数,则让该构造函数调用setupStuff;在最后一个构造函数中删除对 setupStuff 的调用。那么你就不需要doStuff / false 参数了。 (较小的抱怨是,如果您确实有理由使用 doStuff 参数,那么将其设为可为空的布尔值 bool? 没有任何好处。只需使用 bool。)此外,Teejay 指出,这意味着这是有致命缺陷的设计。
    • 更好的代码可能是:public Sample(string theIntAsString) : this(int.Parse(theIntAsString)) {} public Sample(int theInt) { setupStuff(ref _intField, theInt); } 请注意,调用另一个构造函数的第一个构造函数调用setupStuff
    【解决方案5】:

    这是一个调用另一个构造函数的示例,然后检查它设置的属性。

        public SomeClass(int i)
        {
            I = i;
        }
    
        public SomeClass(SomeOtherClass soc)
            : this(soc.J)
        {
            if (I==0)
            {
                I = DoSomethingHere();
            }
        }
    

    【讨论】:

    • 如果您在某些情况下使用默认构造函数并为其他情况进行小/特定更改,这可能会更干净。
    【解决方案6】:

    是的,你可以在 call base 或者 this 之前调用其他方法!

    public class MyException : Exception
    {
        public MyException(int number) : base(ConvertToString(number)) 
        {
        }
    
        private static string ConvertToString(int number) 
        { 
          return number.toString()
        }
    
    }
    

    【讨论】:

    • 只是为了整体回答 - 如果你的构造函数应该初始化任何只读字段,你不能为此使用方法。
    【解决方案7】:

    构造函数链接,即当您想在单个调用中调用多个构造函数时,您可以将“Base”用于 Is 关系,而将“This”用于同一类。

      class BaseClass
    {
        public BaseClass():this(10)
        {
        }
        public BaseClass(int val)
        {
        }
    }
        class Program
        {
            static void Main(string[] args)
            {
                new BaseClass();
                ReadLine();
            }
        }
    

    【讨论】:

      【解决方案8】:

      从基类继承类时,可以通过实例化派生类来调用基类构造函数

      class sample
      {
          public int x;
      
          public sample(int value)
          {
              x = value;
          }
      }
      
      class der : sample
      {
          public int a;
          public int b;
      
          public der(int value1,int value2) : base(50)
          {
              a = value1;
              b = value2;
          }
      }
      
      class run 
      {
          public static void Main(string[] args)
          {
              der obj = new der(10,20);
      
              System.Console.WriteLine(obj.x);
              System.Console.WriteLine(obj.a);
              System.Console.WriteLine(obj.b);
          }
      }
      

      sample program 的输出是

      50 10 20


      您也可以使用this 关键字从另一个构造函数调用构造函数

      class sample
      {
          public int x;
      
          public sample(int value) 
          {
              x = value;
          }
      
          public sample(sample obj) : this(obj.x) 
          {
          }
      }
      
      class run
      {
          public static void Main(string[] args) 
          {
              sample s = new sample(20);
              sample ss = new sample(s);
      
              System.Console.WriteLine(ss.x);
          }
      }
      

      这个sample program的输出是

      20

      【讨论】:

        【解决方案9】:

        错误处理和使您的代码可重用是关键。我在 int 验证中添加了字符串,如果需要,可以添加其他类型。用更可重用的解决方案解决这个问题可能是这样的:

        public class Sample
        {
            public Sample(object inputToInt)
            {
                _intField = objectToInt(inputToInt);
            }
        
            public int IntProperty => _intField;
        
            private readonly int _intField;
        }
        
        public static int objectToInt(object inputToInt)
        {
            switch (inputToInt)
                {
                    case int inputInt:
                        return inputInt;
                    break;
                    case string inputString:
                    if (!int.TryParse(inputString, out int parsedInt))
                    {
                        throw new InvalidParameterException($"The input {inputString} could not be parsed to int");
                    }
                    return parsedInt;
        
                    default:
                        throw new InvalidParameterException($"Constructor do not support {inputToInt.GetType().Name}");
                    break;
                }
        }
        

        【讨论】:

          【解决方案10】:

          如果您需要在调用另一个构造函数之前而不是之后运行某些东西。

          public class Sample
          {
              static int preprocess(string theIntAsString)
              {
                  return preprocess(int.Parse(theIntAsString));
              }
          
              static int preprocess(int theIntNeedRounding)
              {
                  return theIntNeedRounding/100;
              }
          
              public Sample(string theIntAsString)
              {
                  _intField = preprocess(theIntAsString)
              }
          
              public Sample(int theIntNeedRounding)
              {
                  _intField = preprocess(theIntNeedRounding)
              }
          
              public int IntProperty  => _intField;
          
              private readonly int _intField;
          }
          

          如果您需要设置多个字段,ValueTuple 会非常有用。

          【讨论】:

            【解决方案11】:

            请、请、请不要在家里、工作或任何地方尝试这个。

            这是解决一个非常具体的问题的方法,我希望你不要这样。

            我发布这个是因为它在技术上是一个答案,也是另一个角度来看待它。

            我再说一遍,在任何情况下都不要使用它。代码是用 LINQPad 运行的。

            void Main()
            {
                (new A(1)).Dump();
                (new B(2, -1)).Dump();
                
                var b2 = new B(2, -1);
                b2.Increment();
                
                b2.Dump();
            }
            
            class A 
            {
                public readonly int I = 0;
                
                public A(int i)
                {
                    I = i;
                }
            }
            
            class B: A
            {
                public int J;
                public B(int i, int j): base(i)
                {
                    J = j;
                }
                
                public B(int i, bool wtf): base(i)
                {
                }
                
                public void Increment()
                {
                    int i = I + 1;
            
                    var t = typeof(B).BaseType;
                    var ctor = t.GetConstructors().First();
                    
                    ctor.Invoke(this, new object[] { i });
                }
            }
            

            由于构造函数是一个方法,你可以通过反射调用它。现在您要么使用门户进行思考,要么想象一罐蠕虫的图片。对此感到抱歉。

            【讨论】:

              猜你喜欢
              • 2011-03-24
              • 2014-03-12
              • 1970-01-01
              • 2016-07-03
              • 2012-06-22
              • 1970-01-01
              • 1970-01-01
              • 2010-09-22
              相关资源
              最近更新 更多