#c# #data-structures
#c# #структуры данных
Вопрос:
На C я мог бы сделать что-то вроде этого
class Person
{
House * myhouse;
}
class House
{
std::vector<Person*> members;
}
Как я могу сделать аналогичную вещь в C #?
Ответ №1:
public class Person
{
public House MyHouse { get; set; }
}
public class House
{
public List<Person> Members { get; private set; }
public House()
{
this.Members = new List<Person>();
}
}
Вместо полей здесь я использую свойства, в частности, автоматические свойства.
Это делается как для того, чтобы быть более чистым, поскольку предоставление поля внешнему миру обычно менее чисто, чем предоставление свойства, так и потому, что таким образом я могу контролировать, как люди могут получать доступ к свойствам для чтения и для записи. В этом примере свойство Members является общедоступным для чтения, но закрытым для записи, я инициализирую его в конструкторе.
В C # нет понятия объектов, размещаемых в стеке, объекты всегда размещаются в куче.
Это означает, что классы всегда являются ссылочными типами, а переменная типа List является ссылкой на объект типа List, как указатель в C . По этой причине вам необходимо использовать оператор new для его выделения, иначе значение по умолчанию будет равно null.
Конечно, как вы знаете, в C # есть сборщик мусора, поэтому вам не нужно удалять объект.
В C # также существуют типы значений, базовые типы, такие как int, float, double и struct, являются типами значений, и они действительно работают по-другому.
Массивы и строки по-прежнему являются ссылочными типами (классами).
Также обратите внимание, что в классе C # поля по умолчанию инициализируются в конструкторе значением 0, каждый тип, который вы можете придумать, будет инициализирован значением 0, поэтому указатель будет равен null, значение с плавающей точкой будет равно 0.0f, структура будет структурой со всеми полями, равными 0. Как calloc в C.
Однако возможен другой, совершенно отличный подход. Мы можем использовать коллекцию базового класса и сделать свойство myHouse полностью прозрачным и безопасным: мы устанавливаем его при изменении коллекции, этот метод используется часто.
public class Person
{
// This field is internal, it means that all classes in the same module (in the same dll for example) can access to this field.
// This keyword was introduced for the same reason that the "friend" keyword exists in C .
// We need this internal so we can modify it from House class.
internal House house;
public House MyHouse
{
get { return this.house; }
}
}
public class House :
System.Collections.ObjectModel.Collection<Person>
{
// We shadow the base member, this is faster than default implementation, O(1).
public new bool Contains(Person person)
{
return person != null amp;amp; person.house == this;
}
protected override void RemoveItem(int index)
{
Person person = this[index];
base.RemoveItem(index);
person.house = null;
}
protected override void SetItem(int index, Person item)
{
if (item == null)
throw new ArgumentNullException("Person is null");
if (item.house != null)
throw new InvalidOperationException("Person already owned by another house");
Person old = this[index];
base.SetItem(index, item);
old.house = null;
item.house = this;
}
protected override void InsertItem(int index, Person item)
{
if (item == null)
throw new ArgumentNullException("Person is null");
if (item.house != null)
throw new InvalidOperationException("Person already owned by another house");
base.InsertItem(index, item);
item.house = this;
}
protected override void ClearItems()
{
foreach (Person person in this)
{
person.house = null;
}
base.ClearItems();
}
}
Ответ №2:
class Person
{
House myhouse;
}
class House
{
List<Person> members = new List<Person>;
}
Комментарии:
1. Вы никогда не инициализировали участников, вам нужно инициализировать это с помощью = new List<Person>(), это ведет себя иначе, чем C .