В С# вы можете инициализировать свойство коллекции как таковое:
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 установщик удален из автоматически созданных свойств навигации ( объясняется здесь). У нас есть набор тестов, на рефакторинг которого потребуется много времени, в котором есть экземпляры инициализации свойств навигации, как я объяснил выше. Если необходим рефакторинг, пусть будет так, я просто искал языковую функцию, которую мог упустить из виду.
Использовать существующий метод AddRange или написать метод расширения AddRange?
Кстати, ваш пример MyReadonlyContainerClass
не скомпилируется, так как Collection
доступен только для чтения, потому что у него нет сеттера. Если вы добавите сеттер, он скомпилируется, но тогда исходная инициализация ICollection<MyClass> Collection { get; } = new List<MyClass>();
будет отброшена, когда сеттер будет вызван для инициализации Collection
в следующем коде.
@MatthewWatson, автоматически реализуемые в C# свойства только для чтения существуют уже некоторое время, поэтому код будет компилироваться. Они могут быть назначены, как указано выше, или в конструкторе класса. Посмотрите этот ответ stackoverflow.com/a/46381534/8532748 более пяти лет назад. Какую версию С# вы используете?
@Ralf см. обновление для контекста.
@SBFrancies Вот код, о котором я говорил в DotNetFiddle с использованием последней версии компилятора. Он не скомпилируется, потому что пытается присвоить значение свойству Collection
, у которого нет общедоступного установщика.
@MatthewWatson, извините, теперь я понимаю, что вы имеете в виду. Есть ошибка (исправление в вопросе), но это не то, что вы думаете. Я просто исключил ключевое слово public из свойства Collection
. dotnetfiddle.net/AgO7DX
А, просто опечатка.
@SBFrancies Нет решения, если они не вернут сеттер (по крайней мере get; init
) — см. мой комментарий к связанной проблеме GitHub. Но судя по тому, что тема закрыта, а вся "современная" направленность ломания меняется, я на этом копейки не ставлю. Настройте шаблон T4, как предложил EricJ, и позвольте ему сгенерировать сеттер для вас.
Если бы конкретный тип свойства был List<T>
, а не ICollection<T>
, то вы могли бы определить метод расширения с именем Add, который берет коллекцию элементов и вместо этого вызывает метод List
AddRange
:
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
не является частью какого-либо общего интерфейса для коллекций.
спасибо за ответ, к сожалению, я не думаю, что это поможет в этом случае, так как я хочу избежать обновления сгенерированного кода (см. Мое обновление для контекста, который должен был быть в исходном вопросе).
Решение должно работать и с ICollection<T>. Затем метод расширения просто должен зациклиться, поскольку тогда AddRange не существует.
Благодаря более раннему ответу от @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 } };
Относительно простое решение в конце концов, спасибо за помощь.
Вам не подходит конструктор ReadOnlyCollection<T>? Вы ищете коллекцию только для чтения, которая принимает
IEnumerable<T>
в своем конструкторе?