If I want to store the list that the class is being passed as a parameter, what parameter type should I use?
Should I take an IEnumerable and do something like Items = items.ToList().AsReadOnly()
? But how do I avoid superfluous copies? What if the list was created inside the constructor call, that's definitely a ToList()
too many then.
In C++ I would have a non-reference parameter to make sure a copy of the list is passed, but AFAIK in C# that's not possible.
One example I have in mind is an event arguments class that contains a list of something, that is also the current state of the invoking class. The lists should not change after they have been passed to the various event handlers.
Creating a copy inside the constructor is fine when is is called like this:
SomethingHappened?.Invoke(this, new CustomEventArgs(_items));
But when it is called like this, a unnecessary copy is made:
SomethingHappened?.Invoke(this, new CustomEventArgs(new List { item1, item2 }));
If you don't want to modify the list which is passed in to your constructor in your class, then you could define the parameter as an IReadOnlyList<T>
and pass it by calling AsReadOnly()
on the original list. You'll still use the same list, but AsReadOnly()
provides a wrapper which prevents modification, as per the documentation:
public class YourClass
{
private IReadOnlyList<SomeType> yourList;
public YourClass(IReadOnlyList<SomeType> list)
{
yourList = list;
}
}
Then call the constructor as follows:
var someList = new List<SomeType>();
var yourClassInstance = new YourClass(someList.AsReadOnly());
You can still change the original list reference and it will be reflected in the read-only reference, since they're actually accessing the same list, but you won't be able to modify the list inside YourClass
.
Alternatively, if you cannot call AsReadOnly()
on the outside, then you need to call it inside your constructor, which would technically allow your class to make modifications before storing the read-only reference:
public class YourClass
{
private IReadOnlyList<SomeType> yourList;
public YourClass(IList<SomeType> list)
{
yourList = list.AsReadOnly();
}
}
If you do need to modify the passed list after all, you'll need to make a copy, by which YourClass
would then take ownership of the copy:
public class YourClass
{
private IList<SomeType> yourList;
public YourClass(IList<SomeType> list)
{
yourList = new List<T>(list);
}
}
Since the callee cannot know on its own whether a copy should be made, you could add an optional flag to the constructor to transfer the responsibility to the caller:
public class YourClass
{
private IList<SomeType> yourList;
public YourClass(IList<SomeType> list, bool makeCopy = false)
{
yourList = makeCopy ? new List<T>(list) : list;
}
}
This way, the caller must decide whether a copy is required:
var someList = new List<SomeType>();
//Make a copy explicitly
var yourClassInstance1 = new YourClass(someList, true);
//Do not make a copy
var yourClassInstance2 = new YourClass(new List<SomeType>{ object1, object2 });