Вызов базового конструктора в C#

Если я наследую от базового класса и хочу передать что-то из конструктора унаследованного класса конструктору базового класса, как мне это сделать?

Например, если я наследую от класса Exception, я хочу сделать что-то вроде этого:

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

В основном я хочу иметь возможность передавать строковое сообщение в базовый класс Exception.

Также стоит отметить, что вы можете связать конструкторы в своем текущем классе, заменив this на base.

Quibblesome 15.08.2008 17:30

Это здесь docs.microsoft.com/en-us/dotnet/csharp/language-reference/…

DmitryBoyko 29.07.2020 05:48

Вместо того, чтобы говорить "все разваливается", гораздо полезнее просто опубликовать сообщение об ошибке.

JoelFan 11.09.2020 16:35
Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1 614
3
1 121 186
12
Перейти к ответу Данный вопрос помечен как решенный

Ответы 12

Ответ принят как подходящий

Измените свой конструктор на следующее, чтобы он правильно вызывал конструктор базового класса:

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

Обратите внимание, что конструктор - это не то, что вы можете вызывать в любое время внутри метода. Вот почему вы получаете ошибки при вызове в теле конструктора.

Я думаю, вы, возможно, упустили суть. Проблема заключалась в вызове базового конструктора на полпути через переопределенный конструктор. Возможно, тип данных базового конструктора не тот, или вы хотите выполнить некоторую форму данных, прежде чем передавать их по цепочке. Как бы вы совершили такой подвиг?

Marchy 17.02.2009 00:03

Если вам нужно вызвать базовый конструктор в середине переопределения, извлеките его в фактический метод базового класса, который вы можете вызвать явно. При использовании базовых конструкторов предполагается, что они абсолютно необходимы для безопасного создания объекта, поэтому база всегда будет вызываться первой.

Jon Limjap 17.02.2009 13:46

Это является просто метод, который вы можете вызвать в любое время, с точки зрения IL. C# просто накладывает на это дополнительные ограничения.

Roman Starkov 17.04.2011 07:03

Стоит отметить, что конструктор base называется перед, когда осуществляется доступ к блоку метода. msdn.microsoft.com/en-us/library/ms173115.aspx

John Weisz 08.12.2015 19:27

Это не лучший дизайн, если вам нужно вызвать конструктор базового класса на полпути во время работы конструктора. Идея конструктора заключается в том, что он выполняет всю работу, необходимую для выполнения своей задачи. Это приводит к тому, что при запуске производного конструктора базовый класс уже полностью инициализирован, и производный класс может вызывать любую функцию базового класса. Если ваш дизайн таков, что вы хотите сделать что-то наполовину своего конструктора, то, очевидно, это не инициализирует базовый класс, поэтому он не должен находиться в конструкторе базового класса, а в отдельной, возможно, защищенной функции

Harald Coppoolse 16.12.2015 10:28

@HaraldCoppoolse: Я не согласен. Если я правильно помню, iOS Swift на самом деле делает это наоборот: производный класс не должен зависеть от базового класса - это хороший дизайн, вместо этого базовый конструктор вызывается последней строкой в ​​блоке. Без зависимости данных изменение базовых классов не повлияет на производные классы. И обратите внимание, что в определении / механизме перегрузки функций в спецификации языка эта идея принята. (в C++, C#).

Rainning 09.11.2020 10:00

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

Вы можете передать внутреннее исключение одному из конструкторов.

Обратите внимание, что вы можете использовать методы статический в вызове базового конструктора.

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

Класс Exception настолько заблокирован, что я обнаружил, что делаю это пару раз, но также обратите внимание, что это не то, что вам следует делать, если вы можете этого избежать.

Jonathon Cwik 11.03.2015 22:02

Извините, здесь C# newb. Почему вы звоните Trace.WriteLine("message was " + message)?

kdbanman 10.07.2015 01:08

@kdbanman Это просто выводит отладочное сообщение. Нет актуального функционального назначения.

Nick Whaley 21.08.2015 16:52

Отличный ответ. Принятый ответ не позволяет мне выполнять обработку; а последующий комментарий по обходному пути предполагает, что у меня есть доступ для изменения базового класса; Я не. Заводской ответ предполагает, что я могу контролировать создание экземпляра класса; Я не могу. Только ваш ответ позволяет мне что-то изменить перед передачей на базу.

The Red Pea 27.05.2017 00:38

В этом нет абсолютно ничего вредного до тех пор, пока промежуточная функция морально не имеет гражданства. Ведение журнала - не лучший вариант использования, IMO, но нормализация корпуса или добавление дополнительных кажется прекрасным.

Aluan Haddad 21.07.2017 01:47

Если вы попали в описанную выше ситуацию. Вместо наследования вы можете создать свой класс как оболочку базового класса в качестве альтернативного решения.

DonO 10.07.2018 23:31

Если вам нужно вызвать базовый конструктор, но не сразу, потому что ваш новый (производный) класс должен выполнять некоторые манипуляции с данными, лучшим решением будет прибегнуть к фабричному методу. Что вам нужно сделать, так это пометить свой производный конструктор как закрытый, затем создать статический метод в своем классе, который будет делать все необходимое, а затем вызывать конструктор и возвращать объект.

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

Это могло бы потенциально нарушить принципы SOLID (SRP), потому что ответственность за создание класса инкапсулируется с любой другой ответственностью, о которой должен был позаботиться класс. Можно использовать абстрактную фабрику, но это может добавить ненужной сложности к простому коду. Конечно, нарушение SOLID - это нормально, если вы знаете компромисс и ущерб, который он нанесет вашей архитектуре (и как исправить любые будущие проблемы, которые могут возникнуть в результате вашего проектного решения).

Sebastien 11.01.2017 00:22

Верно использовать 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

Я вижу, что вы пытаетесь объяснить, и вы правы. Если у вас есть два конструктора в одном классе, вы можете ссылаться на один из другого, используя ключевое слово this, аналогично тому, как вы используете base при вызове унаследованного конструктора. Однако это не то, о чем просил OP, поэтому здесь не место для добавления этого.

IAmTimCorey 04.12.2013 18:35

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

class MyExceptionClass : Exception
{
     public MyExceptionClass(string message, string extraInfo)
     : base(message)
     {
         [...]
     }
}

Из правил Рекомендации по проектированию каркаса и FxCop.:

1. Имя настраиваемого исключения должно оканчиваться на "Исключение".

    class MyException : Exception

2. Исключение должно быть публичным.

    public class MyException : Exception

3. CA1032: исключение должно реализовывать стандартные конструкторы.

  • Открытый конструктор без параметров.
  • Открытый конструктор с одним строковым аргументом.
  • Открытый конструктор с одной строкой и исключением (так как он может обернуть другое исключение).
  • Конструктор сериализации защищен, если тип не запечатан, и закрытым, если тип запечатан. На основе 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.
      }
    }  

public class MyException : Exception
{
    public MyException() { }
    public MyException(string msg) : base(msg) { }
    public MyException(string msg, Exception inner) : base(msg, inner) { }
}

это лучший ответ, потому что он также содержит перегрузки конструктора.

vibs2006 10.06.2018 05:56

Вы также можете выполнить условную проверку с параметрами в конструкторе, что обеспечивает некоторую гибкость.

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

или же

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

В соответствии с некоторыми другими ответами, перечисленными здесь, вы можете передавать параметры в конструктор базового класса. Рекомендуется вызывать конструктор базового класса в начале конструктора унаследованного класса.

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

Я отмечаю, что в вашем примере вы никогда не использовали параметр extraInfo, поэтому я предположил, что вы, возможно, захотите объединить строковый параметр extraInfo со свойством Message вашего исключения (кажется, что это игнорируется в принятом ответе и коде в ваш вопрос).

Это просто достигается путем вызова конструктора базового класса и последующего обновления свойства Message дополнительной информацией.

public class MyException: Exception
{
    public MyException(string message, string extraInfo) : base($"{message} Extra info: {extraInfo}")
    {
    }
}

Используя новые функции C#, а именно out var, вы можете избавиться от статического фабричного метода. Я только что узнал (случайно), что параметр var методов, называемых без base- "call", передается в тело конструктора.

Пример использования этого базового класса, от которого вы хотите унаследоваться:

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

И решение с использованием статического частного вспомогательного метода, который генерирует все необходимые базовые аргументы (плюс дополнительные данные, если необходимо) и без использования статического фабричного метода, просто внешний конструктор:

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

public class Car
{
     public Car(string model)
     {
        Console.WriteLine(model);
     }
}

public class Mercedes : Car
{
     public Mercedes(string model): base(model)
     {

     }
}

Использование:

Mercedes mercedes = new Mercedes("CLA Shooting Brake");

Продукт: CLA Shooting Brake.

Другие вопросы по теме