在 C 中调用基构造函数#

Calling the base constructor in C#

提问人:lomaxx 提问时间:8/15/2008 最后编辑:John Smithlomaxx 更新时间:11/9/2023 访问量:1383368

问:

如果我从基类继承,并希望将某些内容从继承类的构造函数传递到基类的构造函数,我该怎么做?

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

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

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

C# .NET 继承 构造函数

评论

37赞 Quibblesome 8/15/2008
还值得注意的是,您可以通过替换 .thisbase
0赞 NoWar 7/29/2020
在这里 learn.microsoft.com/en-us/dotnet/csharp/language-reference/...
0赞 گلی 7/18/2021
Try: public class MyExceptionClass : Exception { public MyExceptionClass(string message, string extrainfo) : base(message) { //这里其他东西 } }
0赞 David Mays 2/8/2022
@Quibblesome我还没有找到用.this代替.base的例子。我的措辞可能不正确,但你有什么例子吗?我的问题是,如果您确实进行了替换,您是否不再需要在参数中使用 :base,而只能在构造函数中使用 .base?
0赞 Quibblesome 3/31/2023
@DavidMays我的意思是,与其执行ctor():base(argTheBaseClassNeeds),不如执行ctor():this(DateTime.Now) ctor(DateTime,dt){ /* some impl */ }。在这种情况下,我在当前类中链接一个构造函数,以使默认日期时间值 DateTime.Now。

答:

2213赞 Jon Limjap 8/15/2008 #1

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

public class MyExceptionClass : Exception
{
    public MyExceptionClass(string message, string extrainfo) : base(message)
    {
        //other stuff here
    }
}

请注意,构造函数不是可以在方法中随时调用的东西。这就是您在构造函数正文中的调用中遇到错误的原因。

评论

77赞 Marchy 2/17/2009
我想你可能错过了重点。问题在于在被覆盖构造函数的中途调用基构造函数。也许基构造函数的数据类型不同,或者您想在将其传递到链中之前进行一些数据建模。您将如何完成这样的壮举?
250赞 Jon Limjap 2/17/2009
如果需要在重写过程中调用基构造函数,请将其提取到基类上可以显式调用的实际方法。基构造函数的假设是,它们对于安全地创建对象是绝对必要的,因此将始终首先调用基构造函数。
40赞 Roman Starkov 4/17/2011
它只是一种您可以随时调用的方法,IL-wise。C# 恰好在此基础上施加了额外的限制。
44赞 John Weisz 12/9/2015
值得注意的是,构造函数是在访问方法块之前调用的。msdn.microsoft.com/en-us/library/ms173115.aspxbase
35赞 Harald Coppoolse 12/16/2015
如果需要在构造函数期间中途调用基类构造函数,则这不是一个好的设计。构造函数的思想是它完成完成其任务所需的所有工作。这样做的效果是,当派生构造函数启动时,基类已完全初始化,并且派生类可以自由调用任何基类函数。如果你的设计是这样的,你想在构造函数的一半做一些事情,那么显然这不是在初始化基类,因此不应该在基类的构造函数中,而应该在一个单独的、可能受保护的函数中
35赞 SnowBEE 12/4/2009 #2
public class MyExceptionClass : Exception
{
    public MyExceptionClass(string message,
      Exception innerException): base(message, innerException)
    {
        //other stuff here
    }
}

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

635赞 Axl 4/29/2010 #3

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

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;
     }
}

评论

8赞 Jonathon Cwik 3/12/2015
Exception 类被锁定得如此之多,以至于我确实发现自己这样做了几次,但也要注意,如果可以避免它,这不是你应该做的事情。
4赞 Aluan Haddad 7/21/2017
只要中间功能在道德上是无状态的,这绝对没有什么害处。IMO,日志记录不是一个好的用例,但规范化大小写或添加其他大小写似乎很好。
6赞 DonO 7/11/2018
如果您处于上述情况。您可以构建类作为基类的包装器,而不是继承,作为替代解决方案。
122赞 santahopar 2/27/2013 #4

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

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);
    }
}

评论

12赞 Sebastien 1/11/2017
这可能会违反 SOLID 原则 (SRP),因为创建类的责任与类应该承担的任何其他责任一起封装。可以使用抽象工厂,但可能会给简单代码增加不必要的复杂性。当然,如果您知道权衡及其对架构的影响(以及如何解决设计决策可能产生的任何未来问题),那么违反 SOLID 是可以的。
49赞 Janus Pedersen 11/11/2013 #5

确实,使用 (something) 调用基类构造函数,但在重载的情况下使用关键字basethis

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

评论

12赞 IAmTimCorey 12/4/2013
我明白你想解释什么,你是对的。如果一个类中有两个构造函数,则可以使用“this”关键字从另一个构造函数引用一个构造函数,类似于在调用继承的构造函数时使用“base”的方式。但是,这不是 OP 所要求的,所以这不是真正添加它的地方。
0赞 john k 3/21/2023
the keyword 'base' is not valid in this context
24赞 Fab 1/24/2016 #6

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

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

    class MyException : Exception

2. 例外应该是公开的

    public class MyException : Exception

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.
      }
    }  
    

    [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.
      }
    }  
11赞 A510 4/7/2016 #7
[Serializable]
internal class MyException : Exception
{
    public MyException() {}

    public MyException(string message) : base(message) {}

    public MyException(string message, Exception innerException) : base(message, innerException) {}

    protected MyException(SerializationInfo info, StreamingContext context) : base(info, context) {}
}
22赞 dynamiclynk 5/28/2016 #8

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

public MyClass(object myObject=null): base(myObject ?? new myOtherObject())
{
}

public MyClass(object myObject=null): base(myObject==null ? new myOtherObject(): myObject)
{
}

评论

0赞 David Con 4/6/2022
是否有可能有一个条件来使用 2 个不同的基本构造函数?我的意思是其中一个有 2 个参数,另一个有 3 个参数?
0赞 dynamiclynk 4/6/2022
@DavidCon确定可以在基类中创建多个具有不同 signatures\params 的构造函数,请像上面一样使用它们。
0赞 David Con 4/6/2022
我不能打电话给不同的基本结构者。我需要这样的想法: public MyClass(object myObject=null): base(myObject==null ? invokeConstructorBaseA: invokeConstructorBaseB){} 此外,ConstructorBaseA 有 2 个参数,ConstructorBaseB 有 3 个参数。有什么原因调用它们吗?
17赞 CShark 3/14/2017 #9

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

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

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

这只需调用基类构造函数,然后使用额外信息更新 Message 属性即可实现。

public class MyException: Exception
{
    public MyException(string message, string extraInfo) : base($"{message} Extra info: {extraInfo}")
    {
    }
}
14赞 springy76 3/12/2020 #10

示例,使用要从中派生的此基类:

public abstract class BaseClass
{
    protected BaseClass(int a, int b, int c)
    {
    }
}

要执行的非编译伪代码:

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;
    }
}

2020 版(请参阅下文了解更严格的解决方案)

使用较新的 C# 功能,即 ,您可以摆脱公共静态工厂方法。out var

我只是(偶然地)发现在 base-“call” 中调用的方法的 var 参数流向构造函数主体。(也许这是一个 C# 怪癖,请参阅 C# 1.0 兼容解决方案的 2023 版本)

使用静态私有帮助程序方法,该方法在外部生成所有必需的基本参数(如果需要,还可以生成其他数据),它只是一个公共纯构造函数:

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;
    }
}

2023 更新

您只需要一个额外的私有构造函数和一个附带的私有静态工厂方法,该方法使用与公共 ctor 相同的输入为新的私有构造函数准备数据:

public class DerivedClass : BaseClass
{
    private readonly FatData fatData;

    public DerivedClass(int m)
        : this(PrepareBaseParameters(m))
    {
        Console.WriteLine(this.fatData.ToString());
    }

    private DerivedClass(FatData fd)
        : base(fd.A, fd.B, fd.C)
    {
        this.fatData = fd;
    }

    private static FatData PrepareBaseParameters(int m)
    {
        // might be any (non-async) code which e.x. calls factory methods 
        var fd = new FatData(A: 1 * m, B: 2 * m, C: 3 * m);
        return fd;
    }

    private readonly record struct FatData(int A, int B, int C);
}

不需要特殊的 C# 版本,C#10 记录结构只是为了简短,也可以与任何 C#1.0 类一起使用。

这个版本似乎稍长,但更容易阅读和理解。

评论

0赞 springy76 3/22/2023
已经有评论了——他们怎么了?