Если я наследую от базового класса и хочу передать что-то из конструктора унаследованного класса конструктору базового класса, как мне это сделать?
Например, если я наследую от класса Exception, я хочу сделать что-то вроде этого:
class MyExceptionClass : Exception
{
public MyExceptionClass(string message, string extraInfo)
{
//This is where it's all falling apart
base(message);
}
}
В основном я хочу иметь возможность передавать строковое сообщение в базовый класс Exception.
Это здесь docs.microsoft.com/en-us/dotnet/csharp/language-reference/…
Вместо того, чтобы говорить "все разваливается", гораздо полезнее просто опубликовать сообщение об ошибке.





Измените свой конструктор на следующее, чтобы он правильно вызывал конструктор базового класса:
public class MyExceptionClass : Exception
{
public MyExceptionClass(string message, string extrainfo) : base(message)
{
//other stuff here
}
}
Обратите внимание, что конструктор - это не то, что вы можете вызывать в любое время внутри метода. Вот почему вы получаете ошибки при вызове в теле конструктора.
Я думаю, вы, возможно, упустили суть. Проблема заключалась в вызове базового конструктора на полпути через переопределенный конструктор. Возможно, тип данных базового конструктора не тот, или вы хотите выполнить некоторую форму данных, прежде чем передавать их по цепочке. Как бы вы совершили такой подвиг?
Если вам нужно вызвать базовый конструктор в середине переопределения, извлеките его в фактический метод базового класса, который вы можете вызвать явно. При использовании базовых конструкторов предполагается, что они абсолютно необходимы для безопасного создания объекта, поэтому база всегда будет вызываться первой.
Это является просто метод, который вы можете вызвать в любое время, с точки зрения IL. C# просто накладывает на это дополнительные ограничения.
Стоит отметить, что конструктор base называется перед, когда осуществляется доступ к блоку метода. msdn.microsoft.com/en-us/library/ms173115.aspx
Это не лучший дизайн, если вам нужно вызвать конструктор базового класса на полпути во время работы конструктора. Идея конструктора заключается в том, что он выполняет всю работу, необходимую для выполнения своей задачи. Это приводит к тому, что при запуске производного конструктора базовый класс уже полностью инициализирован, и производный класс может вызывать любую функцию базового класса. Если ваш дизайн таков, что вы хотите сделать что-то наполовину своего конструктора, то, очевидно, это не инициализирует базовый класс, поэтому он не должен находиться в конструкторе базового класса, а в отдельной, возможно, защищенной функции
@HaraldCoppoolse: Я не согласен. Если я правильно помню, iOS Swift на самом деле делает это наоборот: производный класс не должен зависеть от базового класса - это хороший дизайн, вместо этого базовый конструктор вызывается последней строкой в блоке. Без зависимости данных изменение базовых классов не повлияет на производные классы. И обратите внимание, что в определении / механизме перегрузки функций в спецификации языка эта идея принята. (в C++, C#).
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 настолько заблокирован, что я обнаружил, что делаю это пару раз, но также обратите внимание, что это не то, что вам следует делать, если вы можете этого избежать.
Извините, здесь C# newb. Почему вы звоните Trace.WriteLine("message was " + message)?
@kdbanman Это просто выводит отладочное сообщение. Нет актуального функционального назначения.
Отличный ответ. Принятый ответ не позволяет мне выполнять обработку; а последующий комментарий по обходному пути предполагает, что у меня есть доступ для изменения базового класса; Я не. Заводской ответ предполагает, что я могу контролировать создание экземпляра класса; Я не могу. Только ваш ответ позволяет мне что-то изменить перед передачей на базу.
В этом нет абсолютно ничего вредного до тех пор, пока промежуточная функция морально не имеет гражданства. Ведение журнала - не лучший вариант использования, IMO, но нормализация корпуса или добавление дополнительных кажется прекрасным.
Если вы попали в описанную выше ситуацию. Вместо наследования вы можете создать свой класс как оболочку базового класса в качестве альтернативного решения.
Если вам нужно вызвать базовый конструктор, но не сразу, потому что ваш новый (производный) класс должен выполнять некоторые манипуляции с данными, лучшим решением будет прибегнуть к фабричному методу. Что вам нужно сделать, так это пометить свой производный конструктор как закрытый, затем создать статический метод в своем классе, который будет делать все необходимое, а затем вызывать конструктор и возвращать объект.
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 - это нормально, если вы знаете компромисс и ущерб, который он нанесет вашей архитектуре (и как исправить любые будущие проблемы, которые могут возникнуть в результате вашего проектного решения).
Верно использовать 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, поэтому здесь не место для добавления этого.
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) { }
}
это лучший ответ, потому что он также содержит перегрузки конструктора.
Вы также можете выполнить условную проверку с параметрами в конструкторе, что обеспечивает некоторую гибкость.
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.
Также стоит отметить, что вы можете связать конструкторы в своем текущем классе, заменив
thisнаbase.