Можно ли инициализировать свойство коллекции только для чтения, используя коллекцию, а не отдельные элементы в С#?

В С# вы можете инициализировать свойство коллекции как таковое:

public class MyClass
{
    public string Name { get; set ;}
}

public class MyContainerClass
{
    public ICollection<MyClass> Collection { get; set; }
}

var myObject = new MyContainerClass
               {
                   Collection = new List<MyClass>
                                {
                                    new()
                                    {
                                       Name = "1",
                                    },
                                    new()
                                    {
                                       Name = "2",
                                    },
                                },
                            }

Для класса с предварительно созданной коллекцией только для чтения вы можете сделать что-то подобное:

public class MyReadonlyContainerClass
{
    public ICollection<MyClass> Collection { get; } = new List<MyClass>();
}

var myObject = new MyReadonlyContainerClass
               {
                   Collection = {
                                    new()
                                    {
                                       Name = "1",
                                    },
                                    new()
                                    {
                                       Name = "2",
                                    },
                                },
                            };

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

var myCollection = Enumerable.Range(1,10).Select(i => new MyClass{ Name = i.ToString() });

var myObject = new MyReadonlyContainerClass{ Collection = { myCollection } };

Вышеприведенное не компилируется, так как метод Add для List<T> принимает один экземпляр T, а не IEnumerable<T>, но мне было интересно, есть ли синтаксически правильный способ сделать это. Может быть, что-то вроде оператора распространения в javascript?

Обновление для контекста

Я думаю, что из комментариев ниже мне должно было быть яснее, почему я хотел бы решить эту проблему. Я использую EF Core Power Tools для создания классов C# из существующей схемы БД. В версии EF 7 установщик удален из автоматически созданных свойств навигации ( объясняется здесь). У нас есть набор тестов, на рефакторинг которого потребуется много времени, в котором есть экземпляры инициализации свойств навигации, как я объяснил выше. Если необходим рефакторинг, пусть будет так, я просто искал языковую функцию, которую мог упустить из виду.

Вам не подходит конструктор ReadOnlyCollection<T>? Вы ищете коллекцию только для чтения, которая принимает IEnumerable<T> в своем конструкторе?

Matthew Watson 23.01.2023 14:14

Использовать существующий метод AddRange или написать метод расширения AddRange?

Ralf 23.01.2023 14:18

Кстати, ваш пример MyReadonlyContainerClass не скомпилируется, так как Collection доступен только для чтения, потому что у него нет сеттера. Если вы добавите сеттер, он скомпилируется, но тогда исходная инициализация ICollection<MyClass> Collection { get; } = new List<MyClass>(); будет отброшена, когда сеттер будет вызван для инициализации Collection в следующем коде.

Matthew Watson 23.01.2023 14:39

@MatthewWatson, автоматически реализуемые в C# свойства только для чтения существуют уже некоторое время, поэтому код будет компилироваться. Они могут быть назначены, как указано выше, или в конструкторе класса. Посмотрите этот ответ stackoverflow.com/a/46381534/8532748 более пяти лет назад. Какую версию С# вы используете?

SBFrancies 23.01.2023 15:33

@Ralf см. обновление для контекста.

SBFrancies 23.01.2023 15:42

@SBFrancies Вот код, о котором я говорил в DotNetFiddle с использованием последней версии компилятора. Он не скомпилируется, потому что пытается присвоить значение свойству Collection, у которого нет общедоступного установщика.

Matthew Watson 23.01.2023 16:00

@MatthewWatson, извините, теперь я понимаю, что вы имеете в виду. Есть ошибка (исправление в вопросе), но это не то, что вы думаете. Я просто исключил ключевое слово public из свойства Collection. dotnetfiddle.net/AgO7DX

SBFrancies 23.01.2023 16:06

А, просто опечатка.

Matthew Watson 23.01.2023 16:17

@SBFrancies Нет решения, если они не вернут сеттер (по крайней мере get; init) — см. мой комментарий к связанной проблеме GitHub. Но судя по тому, что тема закрыта, а вся "современная" направленность ломания меняется, я на этом копейки не ставлю. Настройте шаблон T4, как предложил EricJ, и позвольте ему сгенерировать сеттер для вас.

Ivan Stoev 23.01.2023 16:44
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
9
68
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Если бы конкретный тип свойства был List<T>, а не ICollection<T>, то вы могли бы определить метод расширения с именем Add, который берет коллекцию элементов и вместо этого вызывает метод ListAddRange:

public static class Program
{
    static void Main(string[] args)
    {
        var myCollection = Enumerable.Range(1, 10).Select(i => new MyClass { Name = i.ToString() });

        var myObject = new MyReadonlyContainerClass { Collection = { myCollection } };
        Console.ReadLine();
    }
}

public class MyReadonlyContainerClass
{
    public List<MyClass> Collection { get; } = new List<MyClass>();
}


public class MyClass
{
    public string Name { get; set; }
}

public static class ConvolutedInit {
    public static void Add<T>(this List<T> collection, IEnumerable<T> items)
    {
        collection.AddRange(items);
    }
}

Тем не менее, я отношу это к области глупых трюков с компилятором, а не к тому, что я бы рекомендовал внедрять в производственный код. Это просто достаточно запутанно, чтобы сбить с толку будущих читателей, а не упростить ситуацию.

Для такой функции нет встроенной поддержки, поскольку AddRange не является частью какого-либо общего интерфейса для коллекций.

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

SBFrancies 23.01.2023 15:43

Решение должно работать и с ICollection<T>. Затем метод расширения просто должен зациклиться, поскольку тогда AddRange не существует.

Ralf 23.01.2023 15:51

Благодаря более раннему ответу от @Damien_The_Unbeliever я смог найти решение. Я добавил метод расширения в интерфейс ICollection.

public static class ICollectionExtensions
{
    public static void Add<T>(this ICollection<T> collection, IEnumerable<T> rangeToAdd)
    {
        foreach (var item in rangeToAdd)
        {
            collection.Add(item);
        }
    }
}

и теперь компилируется следующий код:

var myCollection = Enumerable.Range(1,10).Select(i => new MyClass{ Name = i.ToString() });

var myObject = new MyReadonlyContainerClass{ Collection = { myCollection } };

Относительно простое решение в конце концов, спасибо за помощь.

Другие вопросы по теме