[C#] Свойства-массивы - как работают?

Serpent555

Допустим, есть свойство типа
public int[,] Points {
get {
return points;
}
set {
points = value;
}
}

что именно происходит в момент выполнения присваивания типа Points[i, j] = point? В инете толком ничего не нашел, в книжке своей тоже, калечный мсдн вообще ничего умного как всегда не говорит. Варианты:
1) Берется get'ом временный массив, в нём правится элемент [i, j], затем запихивается set'ом на место. Логично, но очень некрасиво и медленно.
2) Присваивается непосредственно элемент [i, j]. Быстро, но очень нелогично. Ведь в методе set явно прописано присваивание именно массивов.
Таким образом, первый вопрос: как это работает?
Далее, пусть мне хочется сделать свойство, которое работало бы как индексатор, т.е. были бы доступны именно указанные при вызове индексы (например, для эмуляции непрямоугольного поля):
public int[int i, int j] Points { 
set {
if (i > j) {
points[i, j] = value;
}
}
}

Так что второй вопрос звучит так: как это сделать?
Спасибо, жду комментариев.

valkira

Таким образом, первый вопрос: как это работает?
Правильный ответ:
2) Присваивается непосредственно элемент [i, j].
Никаких временных массивов не создается.
Быстро, но очень нелогично. Ведь в методе set явно прописано присваивание именно массивов.
Вообще, рекомендую заглянуть с помощью ILDasm.exe в CIL-код который сгенерит компилятор. Узнаешь много нового. Например то, что свойство Points на самом деле реализуется с помощью двух методов:
public int[,] get_Points;
public void set_Points(int[,] value);

Далее, когда ты пишешь (допустим, класс, реализующий свойство — MyClass)
MyClass x = new MyClass;
x.Points[0,0] = 0;

то метод set_Points не вызывается. Вызывается только get_Points который возвращает ССЫЛКУ на массив points. Совершенно эквивалентный код:
MyClass x = new MyClass;
int[,] a = x.Points;
a[0,0] = 0;

Здесь переменная a ссылается на массив в поле points, то есть на тот же самый объект в памяти. Вот и всё. Отсюда со всей очевидностью следует ответ на твой вопрос. Всё более чем логично.
Второй вопрос не совсем понял. Вообще-то индексаторы по другому реализуются. Там нужно использовать слово this:
public int this[int i, int j] { 
set {
if (i > j) {
points[i, j] = value;
}
}
}

Тогда можно будет обращаться к твоему классу так:
MyClass x = new MyClass;
x[0,0] = 0;
x[0,1] = 7;

Serpent555

Про ответ на первый вопрос - большое спасибо, всё стало ясно.
По поводу второго, как реализовать индексаторы, я, всё-таки, знаю. Мне хотелось, чтобы можно было писать конструкции типа
MyClass x = new MyClass;
x.P[0,0] = 0;
x.P[0,1] = 7;
x.P[1,0] = 5; //А здесь будет, например, ошибка, т.к. проверяется, что первый индекс не должен быть больше второго

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

valkira

А зачем это нужно, если не секрет? Индексатор ведь гораздо проще и элегантнее выглядит. Но если надо сделать именно так, как говоришь, то можно попробовать нечто в роде этого:
public class MyClass
{
int[,] points;
MyProperty prop;

public class MyProperty
{
private MyClass c;

public MyProperty(MyClass c)
{
this.c = c;
}

public int this[int i, int j]
{
set
{
if (i > j) throw new IndexOutOfRangeException;
c.points[i,j] = value; // приватное поле points доступно из этого класса, т.к. он -- внутренний
}
}
}

public MyClass
{
points = new int[5,5];
prop = new MyProperty(this);
}

public MyProperty P
{
get
{
return prop;
}
}
}

Немного извратно, конечно. Но другого способа получить контроль над индексами так сразу не вижу. =/

valkira

Или так:
public class MyClass
{
int[,] points;

public class MyProperty
{
private MyClass c;

public MyProperty(MyClass c)
{
this.c = c;
}

public int this[int i, int j]
{
set
{
if (i > j) throw new IndexOutOfRangeException;
c.points[i,j] = value; // приватное поле points доступно из этого класса, т.к. он -- внутренний
}
}
}

public MyProperty P
{
get
{
return new MyProperty(this);
}
}
}

6yrop

x.P[1,0] = 5; //А здесь будет, например, ошибка, т.к. проверяется, что первый индекс не должен быть больше второго
оператор [] применяется к типу свойства P, поэтому, если хочешь такое, делаешь свой тип (класс) с индексером и свойство P объявляешь этого типа.

valkira

Ну да, собсно это я и написал постом выше.

6yrop

Немного извратно, конечно. Но другого способа получить контроль над индексами так сразу не вижу. =/
делать MyProperty внутренним классом, особого смысла нет.
int[,] points — стоит инкапсулировать в MyProperty
Все остальные методы/свойства массива тупо делегируются к int[,] points
Короче стандартный прием обертки.

6yrop

Ну да, собсно это я и написал постом выше.
когда я писал пост, мне позвонили, поэтому я увидел твои посты уже позже

valkira

делать MyProperty внутренним классом, особого смысла нет.
Смысл в том, что если класс определяется исключительно для нужд другого класса (как в этом примере) и не будет использоваться независимо от него, то правильнее делать его внутренним.
int[,] points — стоит инкапсулировать в MyProperty
Да, правильно.

Serpent555

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

6yrop

нужно это за тем же, зачем и индексатор, но индексатор-то у класса может быть только один
перегруженных может быть несколько.
А может просто метод завести?

Serpent555

хотелось использовать заманчивый интерфейс квадратных скобок
и там должны быть однотипные - несколько двумерных
Оставить комментарий
Имя или ник:
Комментарий: