C# конструктор цепочки? (Как это сделать?)
Я знаю, что это, предположительно, очень простой вопрос, но я уже некоторое время борюсь с этой концепцией. Мой вопрос, как вы цепочки конструкторов в C#? Я в своем первом классе ООП, так что я только учусь. Я не понимаю, как работает построение цепочек или как их реализовать, и даже почему это лучше, чем просто создавать конструкторы без создания цепочек.
Буду признателен за некоторые примеры с объяснением.
Так как же их связать? Я знаю, с двумя это идет:
public SomeClass this: {0}
public SomeClass
{
someVariable = 0
}
Но как ты делаешь это с тремя, четырьмя и так далее?
Опять же, я знаю, что это вопрос новичка, но я изо всех сил пытаюсь понять это, и я не знаю почему.
8 ответов
Вы используете стандартный синтаксис (используя this
как метод), чтобы выбрать перегрузку внутри класса:
class Foo {
private int id;
private string name;
public Foo() : this(0, "") {
}
public Foo(int id, string name) {
this.id = id;
this.name = name;
}
public Foo(int id) : this(id, "") {
}
public Foo(string name) : this(0, name) {
}
}
затем:
Foo a = new Foo(), b = new Foo(456,"def"), c = new Foo(123), d = new Foo("abc");
Обратите внимание также:
- Вы можете связать с конструкторами на основе типа с помощью
base(...)
- Вы можете добавить дополнительный код в каждый конструктор
- по умолчанию (если вы ничего не указали)
base()
Для "почему?":
- сокращение кода (всегда хорошо)
необходимо вызвать нестандартный базовый конструктор, например:
SomeBaseType(int id) : base(id) {...}
Обратите внимание, что вы также можете использовать инициализаторы объектов аналогичным образом (без необходимости что-либо писать):
SomeType x = new SomeType(), y = new SomeType { Key = "abc" },
z = new SomeType { DoB = DateTime.Today };
Я просто хочу поднять действительную точку зрения для любого, кто ищет это. Если вы собираетесь работать с версиями.NET до 4.0 (VS2010), имейте в виду, что вам нужно создавать цепочки конструкторов, как показано выше.
Однако, если вы остаетесь в 4.0, у меня есть хорошие новости. Теперь вы можете иметь один конструктор с необязательными аргументами! Я упросту пример класса Foo:
class Foo {
private int id;
private string name;
public Foo(int id = 0, string name = "") {
this.id = id;
this.name = name;
}
}
class Main() {
// Foo Int:
Foo myFooOne = new Foo(12);
// Foo String:
Foo myFooTwo = new Foo(name:"Timothy");
// Foo Both:
Foo myFooThree = new Foo(13, name:"Monkey");
}
При реализации конструктора вы можете использовать необязательные аргументы, так как были установлены значения по умолчанию.
Я надеюсь, вам понравился этот урок! Я просто не могу поверить, что разработчики жаловались на построение цепочки и не могли использовать необязательные аргументы по умолчанию с 2004/2005! Сейчас в мире разработки потребовалось так много времени, что разработчики боятся его использовать, потому что он не будет обратно совместимым.
Это лучше всего иллюстрируется на примере. Imaging у нас есть класс Person
public Person(string name) : this(name, string.Empty)
{
}
public Person(string name, string address) : this(name, address, string.Empty)
{
}
public Person(string name, string address, string postcode)
{
this.Name = name;
this.Address = address;
this.Postcode = postcode;
}
Итак, здесь у нас есть конструктор, который устанавливает некоторые свойства и использует цепочку конструктора, чтобы позволить вам создать объект только с именем или только с именем и адресом. Если вы создаете экземпляр только с именем, это отправит значение по умолчанию, string.Empty через имя и адрес, которое затем отправит значение по умолчанию для Postcode через конечный конструктор.
При этом вы уменьшаете объем написанного вами кода. На самом деле только один конструктор содержит код, вы не повторяете себя, поэтому, например, если вы меняете Name со свойства на внутреннее поле, вам нужно изменить только один конструктор - если вы установите это свойство во всех трех конструкторах это было бы три места, чтобы изменить это.
Что такое использование "Конструктора Цепи"?
Вы используете его для вызова одного конструктора из другого конструктора.
Как можно реализовать "Конструктор Цепь"?
Используйте ключевое слово ": this (yourProperties)" после определения конструктора. например:
Class MyBillClass
{
private DateTime requestDate;
private int requestCount;
public MyBillClass()
{
/// ===== we naming "a" constructor ===== ///
requestDate = DateTime.Now;
}
public MyBillClass(int inputCount) : this()
{
/// ===== we naming "b" constructor ===== ///
/// ===== This method is "Chained Method" ===== ///
this.requestCount= inputCount;
}
}
Почему это полезно?
Важной причиной является сокращение кодирования и предотвращение дублирования кода. например, повторяющийся код для инициализации свойства Предположим, что некоторое свойство в классе должно быть инициализировано с определенным значением (в нашем примере, requestDate). И класс имеет 2 или более конструктора. Без "Конструктора цепочки" вы должны повторить код инициализации во всех рефераторах класса.
Как это работает? (Или, что такое последовательность выполнения в "Конструкторной цепочке")?
в вышеприведенном примере сначала будет выполнен метод "a", а затем последовательность команд вернется к методу "b". Другими словами, приведенный выше код равен нижнему:
Class MyBillClass
{
private DateTime requestDate;
private int requestCount;
public MyBillClass()
{
/// ===== we naming "a" constructor ===== ///
requestDate = DateTime.Now;
}
public MyBillClass(int inputCount) : this()
{
/// ===== we naming "b" constructor ===== ///
// ===== This method is "Chained Method" ===== ///
/// *** --- > Compiler execute "MyBillClass()" first, And then continue instruction sequence from here
this.requestCount= inputCount;
}
}
У меня есть класс дневника, и поэтому я не пишу настройку значений снова и снова
public Diary() {
this.Like = defaultLike;
this.Dislike = defaultDislike;
}
public Diary(string title, string diary): this()
{
this.Title = title;
this.DiaryText = diary;
}
public Diary(string title, string diary, string category): this(title, diary) {
this.Category = category;
}
public Diary(int id, string title, string diary, string category)
: this(title, diary, category)
{
this.DiaryID = id;
}
Все эти ответы хороши, но я хотел бы добавить примечание о конструкторах с немного более сложной инициализацией.
class SomeClass {
private int StringLength;
SomeClass(string x) {
// this is the logic that shall be executed for all constructors.
// you dont want to duplicate it.
StringLength = x.Length;
}
SomeClass(int a, int b): this(TransformToString(a, b)) {
}
private static string TransformToString(int a, int b) {
var c = a + b;
return $"{a} + {b} = {c}";
}
}
В общем, этот пример можно было бы решить и без этой статической функции, статическая функция допускает более сложную логику или даже вызов методов из другого места.
Вы спрашиваете об этом?
public class VariantDate {
public int day;
public int month;
public int year;
public VariantDate(int day) : this(day, 1) {}
public VariantDate(int day, int month) : this(day, month,1900){}
public VariantDate(int day, int month, int year){
this.day=day;
this.month=month;
this.year=year;
}
}
В цепочке конструктора есть еще один важный момент: порядок. Зачем? Допустим, у вас есть объект, который создается во время выполнения фреймворком, который ожидает его конструктор по умолчанию. Если вы хотите иметь возможность передавать значения, в то же время имея возможность передавать аргументы конструктора, когда это необходимо, это чрезвычайно полезно.
Например, у меня может быть вспомогательная переменная, для которой моим конструктором по умолчанию устанавливается значение по умолчанию, но она может быть перезаписана.
public class MyClass
{
private IDependency _myDependency;
MyClass(){ _myDependency = new DefaultDependency(); }
MYClass(IMyDependency dependency) : this() {
_myDependency = dependency; //now our dependency object replaces the defaultDependency
}
}
Я надеюсь, что следующий пример пролил некоторый свет на цепочку конструктора.
Мой пример использования здесь, например, вы ожидаете, что пользователь передаст каталог вашему конструктору, пользователь не знает, какой каталог передать, и решает разрешить вам назначить каталог по умолчанию. Вы подходите и назначаете каталог по умолчанию, который, по вашему мнению, будет работать.
Кстати, я использовал LINQPad для этого примера, если вам интересно, что такое *.Dump().
ура
void Main()
{
CtorChaining ctorNoparam = new CtorChaining();
ctorNoparam.Dump();
//Result --> BaseDir C:\Program Files (x86)\Default\
CtorChaining ctorOneparam = new CtorChaining("c:\\customDir");
ctorOneparam.Dump();
//Result --> BaseDir c:\customDir
}
public class CtorChaining
{
public string BaseDir;
public static string DefaultDir = @"C:\Program Files (x86)\Default\";
public CtorChaining(): this(null) {}
public CtorChaining(string baseDir): this(baseDir, DefaultDir){}
public CtorChaining(string baseDir, string defaultDir)
{
//if baseDir == null, this.BaseDir = @"C:\Program Files (x86)\Default\"
this.BaseDir = baseDir ?? defaultDir;
}
}