Конструктор для класса с постоянным полем неизменяемой памяти - PullRequest
0 голосов
/ 14 марта 2019

Допустим, у нас есть следующий простой класс.Обратите внимание, что единственное поле - readonly и предназначено для ImmutableList<int>:

class Abc
{
    readonly ImmutableList<int> elts;

    public Abc(params int[] ls) => elts = ImmutableList.CreateRange(ls);
}

. Учитывая показанный конструктор, довольно легко создать экземпляр из некоторых int s:

var result_a = new Abc(10, 20, 30);

Теперь мне также может понадобиться конструктор, который может построить Abc с учетом IEnumerable<int>:

public Abc(IEnumerable<int> ls) => elts = ImmutableList.CreateRange(ls);

Так что наш класс теперь выглядит так:

class Abc
{
    readonly ImmutableList<int> elts;

    public Abc(params int[] ls) => elts = ImmutableList.CreateRange(ls);

    public Abc(IEnumerable<int> ls) => elts = ImmutableList.CreateRange(ls);
}

Это действительно работает:

var ls = new[] { 10, 20, 30 };

var result_b = new Abc(ls);

Однако этот конструктор немного неловкий, потому что на первый взгляд что-то вроде этого:

new Abc(item)

может выглядеть так, как будто он создает Abcс одним элементом (item).Но если item на самом деле IEnumerable<int> с более чем одним элементом, будет вызван второй конструктор, приведенный выше.

Если вы посмотрите на Microsoft ImmutableList API, у них на самом деле есть статический метод, называемый ImmutableList.CreateRange, что аналогично второму конструктору выше.Это хорошо, потому что мы избегаем визуальной неоднозначности, описанной выше.

ОК, поэтому давайте начнем набросать наивную реализацию аналогичного конструктора для нашего Abc класса:

public static Abc CreateRange(IEnumerable<int> ls)
{
    elts = ImmutableList.CreateRange(ls);

    ...
}

OfКонечно, мы столкнулись с проблемой здесь, потому что поле elts равно readonly и не может быть инициализировано с помощью этого статического метода:

enter image description here

Итак,Каков хороший подход для реализации CreateRange для нашего Abc класса?

Ответы [ 2 ]

2 голосов
/ 14 марта 2019

Единственный способ установить elts - вызвать конструктор для Abc.

Модификатор readonly гарантирует единственный способ установить переменную - во время инициализации, readonly int i=0; или в конструкторе.

Ошибка вызвана тем, что статический метод не имеет доступа к экземпляру Abc, хотя вы все равно не сможете его изменить, поскольку это readonly.

Попробуйте вместо этого:

public static Abc CreateRange(IEnumerable<int> ls)
{
   return new Abc(ls);
}
1 голос
/ 14 марта 2019

Я считаю, что SLaks предлагает создать приватный конструктор и вызвать его из статического метода:

private Abc(IEnumerable<int> ls) => elts = ImmutableList.CreateRange(ls);

public static Abc CreateRange(IEnumerable<int> ls) => new Abc(ls);

Таким образом, единственный способ (без отражения) создать экземпляр Abc - это использовать статический метод, при условии, что у вас нет открытых конструкторов.

...