在C#中调用基础构造函数

lom*_*axx 1398 c# inheritance constructor

如果我从一个基类继承并希望将继承类的构造函数中的某些东西传递给基类的构造函数,我该怎么做?

例如,

如果我从Exception类继承,我想做这样的事情:

class MyExceptionClass : Exception
{
     public MyExceptionClass(string message, string extraInfo)
     {
         //This is where it's all falling apart
         base(message);
     }
}
Run Code Online (Sandbox Code Playgroud)

基本上我想要的是能够将字符串消息传递给基本的Exception类.

Jon*_*jap 1736

将构造函数修改为以下内容,以便它正确调用基类构造函数:

public class MyExceptionClass : Exception
{
    public MyExceptionClass(string message, string extrainfo) : base(message)
    {
        //other stuff here
    }
}
Run Code Online (Sandbox Code Playgroud)

请注意,构造函数不是您可以在方法中随时调用的东西.这就是你在构造函数体中调用错误的原因.

  • 如果需要在覆盖的中间调用基础构造函数,则将其提取到可以显式调用的基类上的实际方法.基础构造函数的假设是它们对于安全地创建对象是绝对必要的,因此将始终首先调用基础. (199认同)
  • 我想你可能错过了这一点.问题是关于在重写构造函数中途调用基础构造函数.也许基础构造函数的数据类型不一样,或者您希望在将其传递到链之前进行一些数据成型.你会如何实现这样的壮举? (43认同)
  • 它只是一种你可以随时调用的方法,IL-wise.C#恰好在此基础上加上了额外的限制. (33认同)
  • 如果您需要在构造函数中途调用基类构造函数,那么这不是一个好的设计.构造函数的想法是它完成执行任务所需的所有工作.这样做的结果是,当您的派生构造函数启动时,基类已经完全初始化,派生类可以自由调用任何基类函数.如果你的设计是想要在构造函数中间做某事,那么显然这不是初始化基类ans因此不应该在基类的构造函数中,而是在一个单独的,可能受保护的函数中 (22认同)
  • 值得注意的是,`base`构造函数被称为_before_访问方法块.https://msdn.microsoft.com/en-us/library/ms173115.aspx (18认同)

Axl*_*Axl 490

请注意,您可以在对基础构造函数的调用中使用静态方法.

class MyExceptionClass : Exception
{
     public MyExceptionClass(string message, string extraInfo) : 
         base(ModifyMessage(message, extraInfo))
     {
     }

     private static string ModifyMessage(string message, string extraInfo)
     {
         Trace.WriteLine("message was " + message);
         return message.ToLowerInvariant() + Environment.NewLine + extraInfo;
     }
}
Run Code Online (Sandbox Code Playgroud)

  • @kdbanman那只是输出调试信息.没有相关的功能目的. (5认同)
  • 很好的答案.[接受的答案](/sf/answers/843671/)不允许我做处理; [后续评论的解决方法](/sf/ask/843601/#comment367745_12052)假设我有权更改基类; 我不.[工厂答案](/sf/answers/1057216121/)假设我可以控制类的实例化方式; 我不能.只有你的答案允许我在将其传递给基础之前对其进行修改. (4认同)
  • 只要中间功能在道德上是无状态的,这绝对没有任何害处。在我看来,记录不是一个好的用例,但标准化外壳或添加额外的东西似乎很好。 (4认同)
  • 如果您处于上述情况。作为替代解决方案,您可以将类构建为基类的包装器,而不是继承。 (4认同)
  • Exception类的锁定如此之高,以至于我确实发现自己做了几次,但也请注意,如果可以避免的话,这不是您应该做的事情。 (2认同)

arm*_*ali 95

如果您需要调用基础构造函数但不是立即调用,因为新(派生)类需要进行一些数据操作,最好的解决方案是采用工厂方法.你需要做的是标记私有你的派生构造函数,然后在你的类中创建一个静态方法,它将执行所有必要的东西,然后调用构造函数并返回对象.

public class MyClass : BaseClass
{
    private MyClass(string someString) : base(someString)
    {
        //your code goes in here
    }

    public static MyClass FactoryMethod(string someString)
    {
        //whatever you want to do with your string before passing it in
        return new MyClass(someString);
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 这可能**违反了SOLID原则(SRP),因为创建该类的责任包含在该类假设要处理的任何其他责任中.可以使用抽象工厂,但可能会给简单代码增加不必要的复杂性.当然,如果你知道它会对你的架构产生影响以及如何解决你的设计决策可能引起的任何未来问题,那么违反SOLID就行了. (6认同)

小智 33

确实使用base(某些)来调用基类构造函数,但是在重载的情况下使用this关键字

public ClassName() : this(par1,par2)
{
// do not call the constructor it is called in the this.
// the base key- word is used to call a inherited constructor   
} 

// Hint used overload as often as needed do not write the same code 2 or more times
Run Code Online (Sandbox Code Playgroud)

  • 我明白你要解释的是什么,你是对的.如果在一个类中有两个构造函数,则可以使用"this"关键字引用另一个,类似于在调用继承的构造函数时如何使用"base".但是,这不是OP要求的,所以这不是真正的添加它的地方. (8认同)

Sno*_*BEE 26

public class MyExceptionClass : Exception
{
    public MyExceptionClass(string message,
      Exception innerException): base(message, innerException)
    {
        //other stuff here
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以将内部异常传递给其中一个构造函数.


Fab*_*Fab 19

来自框架设计指南和FxCop规则.:

1.自定义异常的名称应以Exception结尾

    class MyException : Exception
Run Code Online (Sandbox Code Playgroud)

2.例外应该是公开的

    public class MyException : Exception
Run Code Online (Sandbox Code Playgroud)

3. CA1032:异常应该实现标准构造函数.

  • 公共无参数构造函数.
  • 具有一个字符串参数的公共构造函数.
  • 一个带有一个字符串和Exception的公共构造函数(因为它可以包装另一个Exception).
  • 序列化构造函数在类型未密封时受到保护,如果类型被密封则为私有.基于MSDN:

    [Serializable()]
    public class MyException : Exception
    {
      public MyException()
      {
         // Add any type-specific logic, and supply the default message.
      }
    
      public MyException(string message): base(message) 
      {
         // Add any type-specific logic.
      }
      public MyException(string message, Exception innerException): 
         base (message, innerException)
      {
         // Add any type-specific logic for inner exceptions.
      }
      protected MyException(SerializationInfo info, 
         StreamingContext context) : base(info, context)
      {
         // Implement type-specific serialization constructor logic.
      }
    }  
    
    Run Code Online (Sandbox Code Playgroud)

要么

    [Serializable()]
    public sealed class MyException : Exception
    {
      public MyException()
      {
         // Add any type-specific logic, and supply the default message.
      }

      public MyException(string message): base(message) 
      {
         // Add any type-specific logic.
      }
      public MyException(string message, Exception innerException): 
         base (message, innerException)
      {
         // Add any type-specific logic for inner exceptions.
      }
      private MyException(SerializationInfo info, 
         StreamingContext context) : base(info, context)
      {
         // Implement type-specific serialization constructor logic.
      }
    }  
Run Code Online (Sandbox Code Playgroud)


wch*_*ard 15

您还可以在构造函数中使用参数进行条件检查,这允许一些灵活性.

public MyClass(object myObject=null): base(myObject ?? new myOtherObject())
{
}
Run Code Online (Sandbox Code Playgroud)

要么

public MyClass(object myObject=null): base(myObject==null ? new myOtherObject(): myObject)
{
}
Run Code Online (Sandbox Code Playgroud)


Tut*_*men 8

class Exception
{
     public Exception(string message)
     {
         [...]
     }
}

class MyExceptionClass : Exception
{
     public MyExceptionClass(string message, string extraInfo)
     : base(message)
     {
         [...]
     }
}
Run Code Online (Sandbox Code Playgroud)


CSh*_*ark 8

根据此处列出的其他一些答案,您可以将参数传递给基类构造函数.建议在继承类的构造函数的开头调用基类构造函数.

public class MyException : Exception
{
    public MyException(string message, string extraInfo) : base(message)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

我注意到在您的示例中您从未使用过该extraInfo参数,因此我假设您可能希望将extraInfo字符串参数连接到Message异常的属性(似乎在接受的答案和问题中的代码中忽略了这一点) .

这可以通过调用基类构造函数,然后使用额外信息更新Message属性来实现.

或者,由于extraInfo属性是从基类继承的,因此您甚至不必显式调用基类构造函数.您可以extraInfo直接从继承类的构造函数更新属性,如下所示:

public class MyException: Exception
{
    public MyException(string message, string extraInfo) : base($"{message} Extra info: {extraInfo}")
    {
    }
}
Run Code Online (Sandbox Code Playgroud)


spr*_*y76 8

使用较新的 C# 特性,即out var,您可以摆脱静态工厂方法。我刚刚发现(偶然)称为 inse base-“call” 的方法的 out var 参数流向构造函数体。

例如,使用您想要派生的这个基类:

public abstract class BaseClass
{
    protected BaseClass(int a, int b, int c)
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

要执行的非编译伪代码:

public class DerivedClass : BaseClass
{
    private readonly object fatData;

    public DerivedClass(int m)
    {
        var fd = new { A = 1 * m, B = 2 * m, C = 3 * m };
        base(fd.A, fd.B, fd.C); // base-constructor call
        this.fatData = fd;
    }
}
Run Code Online (Sandbox Code Playgroud)

解决方案是使用静态私有辅助方法,该方法生成所有必需的基本参数(如果需要,还可以添加其他数据),并且不使用静态工厂方法,只需向外部简单构造函数即可:

public class DerivedClass : BaseClass
{
    private readonly object fatData;

    public DerivedClass(int m)
        : base(PrepareBaseParameters(m, out var b, out var c, out var fatData), b, c)
    {
        this.fatData = fatData;
        Console.WriteLine(new { b, c, fatData }.ToString());
    }

    private static int PrepareBaseParameters(int m, out int b, out int c, out object fatData)
    {
        var fd = new { A = 1 * m, B = 2 * m, C = 3 * m };
        (b, c, fatData) = (fd.B, fd.C, fd); // Tuples not required but nice to use
        return fd.A;
    }
}
Run Code Online (Sandbox Code Playgroud)


Don*_*sin 7

public class MyException : Exception
{
    public MyException() { }
    public MyException(string msg) : base(msg) { }
    public MyException(string msg, Exception inner) : base(msg, inner) { }
}
Run Code Online (Sandbox Code Playgroud)

  • 这是最好的答案,因为它也包含构造函数重载。 (3认同)