Нельзя добавить конструктор только для Weight или только для Age
Являюсь новичком в C#, не могу разобраться с использованием this в конструкторе, не могу понять почему нельзя добавить конструктор только для Weight или только для Age. За более развернутый ответ или отсылку к хорошему материалу буду премного благодарен!
class Person
{
int Age, Weight;
string Name;
public Person() : this(2, 3, "") { }
public Person(int weight) : this(2, weight, "") { }
public Person(int age) : this(age, 3, "") { }
public Person(string name) : this(2, 3, name) { }
public Person(int Age, int Weight, string Name)
{
this.Age = Age;
this.Name = Name;
this.Weight = Weight;
}
public void InfoName()
{
Console.WriteLine($"name: {Name}, age: {Age}, weight: {Weight}");
}
}
Ответы (4 шт):
Потому это перегрузка методов в вашем случае конструкторов.
Note: Multiple methods can have the same name as long as the number and/or type of parameters are different.
Несколько методов могут иметь одно и то же наименование, если количество либо тип параметров различны.
Ваш вопрос касается сразу нескольких тем.
Во-первых, код не компилируется, т.к. age и weight имеют одинаковый тип и при вызове невозможно понять, что имел ввиду программист: вызов конструктора с int weight или вызов конструктора с int weight, вот угадайте какой конструктор из этих двух я хочу вызовать:
var person = new Person(1);
Они одинаковые, с одинаковым типом. Компилятор не угадает и никто не угадает.
Во-вторых, крайне плохо в программировании использование неинициализированных переменных, когда объявили переменную, но начального значения в ней нет. Поэтому конструировать класс человека у которого при входе известен только age или только weight нужно аккуратно. Хорошей практикой является использование nullable-значений: объявив age как int? вы честно указываете, что можете не знать возраст. Когда же вы назначаете некоторое значение возраста некоторой магической константе 2 - вы не сможете потом отличить действительно ли вы не знаете возраст или человеку действительно два года.
В-третьих, конструкторы -- это хорошее место для того, чтобы гарантировать создание класса с валидными параметрами. Можно попробовать нашпиговать ваш класс различными вариациями конструкторов, но часто в подобных случаях более удобно использовать паттерн Строитель.
Например, в таком виде:
var person = new Person()
.SetAge(10)
.SetWeight(20);
или в более обобщённом:
var person = new Person()
.Set(x => x.Age, 10)
.Set(x => x.Weight, 20);
Это называется перегрузка конструктора, у вас есть разные конструкторы, одного класса, когда вы вызываете new <class name>(<args>).
В зависимости от переданных аргументов, будет вызван соответствующий конструктор.
Person p = new Person(); будет вызван конструктор без аргументов, если вы вызовите
Person p = new Person(10); будет вызван конструктор который принимает один аргумент Int, таким образом вы меняете поведение конструктора в зависимости от переданных аргументов, надеюсь этот пример поможет понять.
namespace Rextester
{
class Person {
int Age, Weight;
string Name;
// первый конструктор, будет вызван если не передать аргументов.
public Person(){
this.Age = 18;
this.Weight = 80;
this.Name = "default";}
// второй, ожидает только один аргумент тика Int
public Person(int Age){
this.Age = Age;}
// третий, ожидает три аргумента и тд
public Person(int Age, int Weight, string Name)
{
this.Age = Age;
this.Name = Name;
this.Weight = Weight;
}
public void InfoName()
{
Console.WriteLine($"name: {Name}, age: {Age}, weight: {Weight}");
}
}
public class Program
{
public static void Main(string[] args)
{
//Мы создаём объест класса Person 3 разными конструкторами, просто передавая разные аргументы.
Person p = new Person();
p.InfoName();
p = new Person(13, 40, "Mike");
p.InfoName();
p = new Person(15);
p.InfoName();
}
}
}
вывод
name: default, age: 18, weight: 80
name: Mike, age: 13, weight: 40
name: , age: 15, weight: 0
А ключевое слово this это ссылка на текущий экземпляр объекта, способ объекту получить доступ к именно своим переменным/методам и изменить/вывести/вызвать их.
В программировании есть такое понятие, как запах проектирования - design smell.
Это не серьёзная ошибка, но недостаток, который мешает пониманию и дальнейшему развитию кода.
Одним из них является одержимость примитивными типами - primitive obsession. По этим терминам можете легко найти подробные описания.
Рассмотрим ваши weight и age - они оба являются простыми типами: int. Что мы можем сказать, глядя на них? Какое знание можно почерпнуть из того факта, что это целые числа? Кстати, вес вполне можно сделать вещественным числом, но сейчас речь не об этом.
В чём измеряется вес вашего человека (Person): в килограммах, фунтах, ньютонах? А возраст? Кто может дать гарантию, что это именно года, а не месяцы, дни, миллисекунды (unix-way)?
Введём два класса для хранения возраста и веса.
При создании их экземпляров в конструкторе делаются проверки на допустимые значения.
Они могут содержать дополнительные методы и свойства. Например, свойство Pounds выдаёт вес в фунтах.
class Age
{
public Age(int years)
{
if (years < 0)
throw new ArgumentOutOfRangeException(nameof(years), "Возраст не может быть меньше 0 лет");
if (years > 300)
throw new ArgumentOutOfRangeException(nameof(years), "Возраст не может быть больше 300 лет");
Years = years;
}
public int Years { get; }
}
class Weight
{
public Weight(float kilograms)
{
if (kilograms < 0)
throw new ArgumentOutOfRangeException(nameof(kilograms), "Вес не может быть меньше 0 кг");
if (kilograms > 300)
throw new ArgumentOutOfRangeException(nameof(kilograms), "Вес не может быть больше 1000 кг");
Kilograms = kilograms;
}
public float Kilograms { get; }
public float Pounds => Kilograms / 0.454f; // фунты
}
Теперь мы можем использовать эти классы в других классах.
class Person
{
public Person() : this(new Age(0), new Weight(0), "noname") { }
public Person(Age age) : this(age, new Weight(0), "noname") { }
public Person(Weight weight) : this(new Age(0), weight, "noname") { }
public Person(string name) : this(new Age(0), new Weight(0), name) { }
public Person(Age age, Weight weight, string name)
{
Age = age ?? throw new ArgumentNullException(nameof(age));
Weight = weight ?? throw new ArgumentNullException(nameof(weight));
Name = name ?? throw new ArgumentNullException(nameof(name));
}
public Age Age { get; }
public Weight Weight { get; }
public string Name { get; }
public void InfoName()
{
Console.WriteLine($"Name: {Name}, Age: {Age.Years}, Weight: {Weight.Kilograms}");
}
}
Теперь нет никаких проблем с созданием перегрузок конструкторов, принимающих любое сочетание параметров. Ведь Age и Weight - это разные классы.
Естественно, для имени тоже можно создать свой класс, который будет проверять значение на длину и допустимые символы.
При этом внутри Person не нужно делать проверки на допустимый диапазон значений - эти проверки уже сделаны внутри Age и Weight.
var person1 = new Person();
person1.InfoName();
var person2 = new Person(new Age(33), new Weight(55), "Маша");
person2.InfoName();
Console.WriteLine(person2.Weight.Pounds);
Конечно, такой подход тоже имеет недостатки: создание большого количества мелких типов, служащих лишь для хранения элементарных данных. Его вполне можно назвать оверинжинирингом. Применять его или нет - выбор за вами.