What exactly does it mean when assigning new() to a property?
I found some examples of seeing new usage in method calls but not like the below.
public ObservableCollection<Customer> Customers { get; } = new();
It's target-typed new, essentially it will create an object of whatever the left of the operand is.
In the case of auto-properties, it will assign a new instance of the type of that property, to the property.
So if we strip away all of the syntactic sugar, what you've essentially got is:
private ObservableCollection<Customer> _customers = new ObservableCollection<Customer>();
public ObservableCollection<Customer> Customers
{
get
{
return _customers;
}
}
Incidentally, you can use a target-typed new almost anywhere there's a well-known type, not just on auto-properties.
This is a target-typed new expression (introduced in C# 9) and is equivalent to:
public ObservableCollection<Customer> Customers { get; } = new ObservableCollection<Customer>();
So the Customers is an auto implemented property which initialized to a newly created instance of ObservableCollection<Customer>.
Related
What's the difference between:
public List<MyType> Something{ get; set; } = new List<MyType>();
and
public List<MyType> Something{
get{
return new List<MyType>();
}
//set...
}
Context:
I'm unsure of the behaviour I'm seeing in my code. A service is there on constructor, but null on a future method call in the what I assume is the same instance of the class.
The first line:
public List<MyType> Something{ get; set; } = new List<MyType>();
will be called once when the object (that has this property) is instantiated. It is a one time creation of an instance of Something.
The second example is an explicit implementation of the getter. Every time you access the getter of Something it will return a new and empty list.
EDIT:
The first line is called an auto-property initializer for a detailed answer have a look at a post by Jon Skeet. This feature exists since C# 6.0
Recently, I came across some code that looked like this:
public class Test
{
public ICollection<string> Things { get; set; }
public Test()
{
Things = new List<string> { "First" };
}
public static Test Factory()
{
return new Test
{
Things = { "Second" }
};
}
}
Calling Test.Factory() results in a Test object with a Things collection containing both "First" and "Second".
It looks like the line Things = { "Second" } calls the Add method of Things. If the ICollection is changed to an IEnumerable, there is a syntax error stating "IEnumerable<string> does not contain a definition for 'Add'".
It is also apparent that you can only use this kind of syntax in an object initialiser. Code such as this is invalid:
var test = new Test();
test.Things = { "Test" };
What is the name of this feature? In which version of C# was it introduced? Why is it only available in object initialisers?
It is called a collection initializer and it was added in the C# 3 language specifications (section 7.5.10.3 at introduction, section 7.6.10.3 in the current specs). To be specific, the code you use uses an embedded collection initializer.
Collection initializer actually just call the Add method, which is required according to the specs.
As Quantic commented, the specs say:
A member initializer that specifies a collection initializer after the equals sign is an initialization of an embedded collection. Instead of assigning a new collection to the field or property, the elements given in the initializer are added to the collection referenced by the field or property.
That explains your unexpected results quite good.
Why is it only available in object initialisers?
Because it doesn't make sense elsewhere. You could just call the Add method yourself instead of using initializers for something else than initializing.
As Patrick already mentioned the collection initializer sequentially calls Add on the list. This assumes your property has been initialized by a constructor accordingly:
public class MyClass
{
public List<MyType> TheList { get; private set; }
public MyClass()
{
this.TheList = new List<MyType>();
}
}
If there is no such constructor that initializes your list you'll get a NullReferenceException in the following statement:
test.Things = { "Test" };
However this is something different than the following:
var m = new MyClass { TheList = new List<MyType> { ... } };
in which case you'll access the properties setter. Having none (or only a private one as in my example) leads to a compiler-error.
I have a class called Package, in this class I have around 10 attributes, let's call them 1,2,3,4.. etc to 10. The types of these attributes are strings, ints and some DateTimes. When making a new object of Package sometimes I just need attribute 1, sometimes I need 5, 6 and 9, and sometimes I need 3 and 10 etc.
So just two examples: new Package("bla", "bla bla",100) or new Package(2983)
I've read:
An interface looks like a class, but has no implementation. The only
thing it contains are declarations of events, indexers, methods and/or
properties. The reason interfaces only provide declarations is because
they are inherited by classes and structs, which must provide an
implementation for each interface member declared.
Since there are no other methods in the class and just a constructor and attributes, is it better to use like 20 constructors or should I make an interface for this situation?
EDIT:
I should've probably mentioned that I also have some enums to 'kind of' determine what kind of Package it is.
An interface doesn't help you in any way here.
If you want to force that specific variables are filled in together, like 1, 2 and 3 should always be filled together but in another case just 4 is enough, you could use separate constructors, or static methods with helpful names that create the objects (like CreateFromId, CreateFromNameAndAge).
If you don't care at all, you can simply make a parameterless constructor (or a constructor with optional fields) and set the fields required with object initializers:
var x = new Class() { Field1 = 1, Field2 = "2" };
Maybe this is a sign you are doing too much in a single object, but without actual information about your class design, we can't tell that much.
Inheritance seems to be a decent solution here too, if the packages have distinct uses (like ProductPackage, PersonPackage, etc.). The shared properties reside in the base class, and all specific properties can reside in the deriving classes.
Constructors provide guidelines as to how can an object be created. Assuming that by using an interface you mean specify the properties which need to exist, you are not giving any guidelines as to how properties need to be initialized.
Having multiple constructors should be better since you are providing means in which users can instantiate your objects. This will allow you to initialize your other parameters accordingly.
You could still use an interface if you require to stipulate what fields need to exist.
Besides above points, consider using Builder pattern - https://en.wikipedia.org/wiki/Builder_pattern
Here is the example:
class Package
{
public string Name { get; set; }
public string Description { get; set; }
public string Prop1 { get; set; }
public string Prop2 { get; set; }
public static PackageBuilder Create()
{
return new PackageBuilder(new Package());
}
}
class PackageBuilder
{
private readonly Package _package;
public PackageBuilder(Package package)
{
_package = package;
}
public PackageBuilder WithName(string name)
{
_package.Name = name;
return this;
}
public PackageBuilder WithDescription(string description)
{
_package.Description = description;
return this;
}
public PackageBuilder Prop1(string prop)
{
_package.Prop1 = prop;
return this;
}
public PackageBuilder Prop2(string prop)
{
_package.Prop2 = prop;
return this;
}
public static implicit operator Package(PackageBuilder pb)
{
return pb._package;
}
}
class Client
{
Package BuildPackage()
{
var package =
Package.Create().WithName("My Package").WithDescription("Description").Prop1("foo").Prop2("bar");
return package;
}
}
I'm only using Code Analysis for cleaning, organizing and ensuring these changes are globally performed for all instances of a particular warning. I'm down to the final, and it's CA2227.
CA2227 Collection properties should be read only Change '' to be
read-only by removing the property setter.
Note this is for mapping of EDI documents. These classes are to represent a whole or part of an EDI document.
public class PO1Loop
{
public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1 { get; set; }
public Collection<SegmentTypes.PO1LoopSegmentTypes.PID1> PIDRepeat1 { get; set; }
public Collection<SegmentTypes.PO1LoopSegmentTypes.PID2> PIDRepeat2 { get; set; }
public SegmentTypes.PO1LoopSegmentTypes.PO4 PO4 { get; set; }
/* Max Use: 8 */
public Collection<SegmentTypes.PO1LoopSegmentTypes.ACK> ACKRepeat { get; set; }
}
You can see all of the Collection properties will give me this warning, and there are hundreds of them. When using the above class I instantiate it without any data. Then externally I add the data and set each individual variable through its public accessor. I do not instantiate this class with all the data prepared and passed using a constructor method (IMO for the size these can reach it can easily wreak havoc on the eyes). When complete and all properties are assigned the class as a whole is then used to generate that part of a document it represents.
My question is, for the usage described above, what would be a better approach for setting this up correctly? Do I keep the public accessors and suppress this warning entirely, or is there a entirely different solution that would work?
Here's what MSDN says about the error, and also how you can avoid it.
Here's my take on the issue.
Consider, the following class:
class BigDataClass
{
public List<string> Data { get; set; }
}
This class will throw that exact same issue. Why? Because Collections do not need a setter. Now, we can do anything with that object: assign Data to an arbitrary List<string>, add elements to Data, remove elements from Data, etc. If we remove the setter, we only lose the ability to directly assign to that property.
Consider the following code:
class BigDataClass
{
private List<string> data = new List<string>();
public List<string> Data { get { return data; } } // note, we removed the setter
}
var bigData = new BigDataClass();
bigData.Data.Add("Some String");
This code is perfectly valid and in fact the recommended way to do things. Why? Because the List<string> is a reference to a memory location, that contains the remainder of the data.
Now, the only thing you cannot now do with this, is directly set the Data property. I.e. the following is invalid:
var bigData = new BigDataClass();
bigData.Data = new List<string>();
This is not necessarily a bad thing. You'll notice that on many .NET types this model is used. It's the basics of immutability. You usually do not want direct access to the mutability of Collections, as this can cause some accidental behavior that has strange issues. This is why Microsoft recommends you omit setters.
Example:
var bigData = new BigDataClass();
bigData.Data.Add("Some String");
var l2 = new List<string>();
l2.Add("String 1");
l2.Add("String 2");
bigData.Data = l2;
Console.WriteLine(bigData.Data[0]);
We might be expecting Some String, but we'll get String 1. This also means that you cannot reliably attach events to the Collection in question, so you cannot reliably determine if new values are added or values are removed.
A writable collection property allows a user to replace the collection with a completely different collection.
Essentially, if you only ever need to run the constructor, or assignment, once, then omit the set modifier. You won't need it, direct assignment of collections is against best-practices.
Now, I'm not saying never use a setter on a Collection, sometimes you may need one, but in general you should not use them.
You can always use .AddRange, .Clone, etc. on the Collections, you only lose the ability of direct assignment.
Serialization
Lastly, what do we do if we wish to Serialize or Deserialize a class that contains our Collection without a set? Well, there is always more than one way to do it, the simplest (in my opinion) is to create a property that represents the serialized collection.
Take our BigDataClass for example. If we wished to Serialize, and then Deserialize this class with the following code, the Data property would have no elements.
JavaScriptSerializer jss = new JavaScriptSerializer();
BigDataClass bdc = new BigDataClass();
bdc.Data.Add("Test String");
string serd = jss.Serialize(bdc);
Console.WriteLine(serd);
BigDataClass bdc2 = jss.Deserialize<BigDataClass>(serd);
So, to fix this, we can simply modify our BigDataClass a bit to make it use a new string property for Serialization purposes.
public class BigDataClass
{
private List<string> data = new List<string>();
[ScriptIgnore]
public List<string> Data { get { return data; } } // note, we removed the setter
public string SerializedData { get { JavaScriptSerializer jss = new JavaScriptSerializer(); return jss.Serialize(data); } set { JavaScriptSerializer jss = new JavaScriptSerializer(); data = jss.Deserialize<List<string>>(value); } }
}
Another option is always the DataContractSerializer (which is really a better option, in general.) You can find information about it on this StackOverflow question.
With current VS2019 we can simply do this:
public List<string> Data { get; } = new List<string>();
This satisfies CA2227 and can be serialized/deserialized.
The deserialization works because List<> has an "Add" method, and the serializer knows how to handle a read-only collection property with an Add method (the property is read-only but not the elements) (I use Json.Net, other serializers may behave differently).
Edit:
As pointed out it should be "=" and not "=>" (compiler will prevent you using "=>"). If we used "public List Data => new List();" then it would create a new list every time the property was accessed which is not what we want either.
Edit:
Note that this will NOT work if the type of the property is an interface, such as IList
Edit:
I think the handling of interfaces is determined by the serializer used. The following works perfectly. I'm sure all common serializers know how to handle ICollection. And if you have some custom interface that does not implement ICollection then you should be able to configure the serializer to handle it, but in that case CA2227 probably won't be triggered making it irrelevant here. (As it is a read-only property you have to assign a concrete value within the class so it should always be serializing and de-serializing a non-null value)
public class CA2227TestClass
{
public IList Data { get; } = new List<string>();
}
[TestMethod]
public void CA2227_Serialization()
{
var test = new CA2227TestClass()
{
Data = { "One", "Two", "Three" }
};
var json = JsonConvert.SerializeObject(test);
Assert.AreEqual("{\"Data\":[\"One\",\"Two\",\"Three\"]}", json);
var jsonObject = JsonConvert.DeserializeObject(json, typeof(CA2227TestClass)) as CA2227TestClass;
Assert.IsNotNull(jsonObject);
Assert.AreEqual(3, jsonObject.Data.Count);
Assert.AreEqual("One", jsonObject.Data[0]);
Assert.AreEqual("Two", jsonObject.Data[1]);
Assert.AreEqual("Three", jsonObject.Data[2]);
Assert.AreEqual(typeof(List<string>), jsonObject.Data.GetType());
}
💡 Alternative Solution 💡
In my situation, making the property read-only was not viable because the whole list (as a reference) could change to a new list.
I was able to resolve this warning by changing the properties' setter scope to be internal.
public List<Batch> Batches
{
get { return _Batches; }
internal set { _Batches = value; OnPropertyChanged(nameof(Batches)); }
}
Note one could also use private set...
The hint's (achilleas heal) of this warning seems really pointed to libraries for the documentation says (Bolding mine):
An externally visible writable property is a type that implements
System.Collections.ICollection.
For me it was, "Ok, I won't make it viewable externally...." and internal was fine for the app.
Thanks to #Matthew, #CraigW and #EBrown for helping me understanding the solution for this warning.
public class PO1Loop
{
public SegmentTypes.PO1LoopSegmentTypes.PO1 PO1 { get; set; }
public Collection<SegmentTypes.PO1LoopSegmentTypes.PID1> PIDRepeat1 { get; private set; }
public Collection<SegmentTypes.PO1LoopSegmentTypes.PID2> PIDRepeat2 { get; private set; }
public SegmentTypes.PO1LoopSegmentTypes.PO4 PO4 { get; set; }
/* Max Use: 8 */
public Collection<SegmentTypes.PO1LoopSegmentTypes.ACK> ACKRepeat { get; private set; }
public PO1Loop()
{
PIDRepeat1 = new Collection<SegmentTypes.PO1LoopSegmentTypes.PID1>();
PIDRepeat2 = new Collection<SegmentTypes.PO1LoopSegmentTypes.PID2>();
ACKRepeat = new Collection<SegmentTypes.PO1LoopSegmentTypes.ACK>();
}
}
When wanting to assign data to the collection types use AddRange, Clear or any other variation of method for modifying a collection.
Only while binding DTO, you need to suppress warnings.
otherwise a custom ModelBinder is required custom ModelBinder to bind collections.
quoting the rule documentation:
When to suppress warnings
You can suppress the warning if the property is part of a Data Transfer Object (DTO) class.
Otherwise, do not suppress warnings from this rule.
https://learn.microsoft.com/pt-br/visualstudio/code-quality/ca2227?view=vs-2019
DTOs often require serialization and deserialization. Thus, they are required to be mutable.
Having to create an alternate backing property is a pain.
Simply change the property type from List<string> to IReadOnlyList<string> then this works as expected without CA2227.
The collection is set via the property but you can also cast to List<string> if you wish to append or delete items.
class Holder
{
public IReadOnlyList<string> Col { get; set; } = new List<string>();
}
var list = new List<string> { "One", "Two" };
var holder = new Holder() { Col = list } ;
var json = JsonConvert.SerializeObject(holder);
// output json {"Col":["One","Two"]}
var deserializedHolder = JsonConvert.DeserializeObject<Holder>(json);
I had to fix some of the CA2227 violations, so i had to add the "readonly" keyword to the collection field and then of course, had to remove the setter property. Some code that have used the setter, just created a new collection object which initially was empty. This code sure did not compile so i had to add a SetXxx() method in order to realize the missing setter's functionality. I did it like this:
public void SetXxx(List<string> list)
{
this.theList.Clear();
this.theList.AddRange(list);
}
The code of callers using the setter has been replaced with a call to the method SetXxx().
Instead of creating a complete new list, the existing list now will be cleared and filled with new items from another list, passed in as a parameter. The original list, due to the fact it is readonly and created only once, will always remain.
I believe this is also a good way to avoid that the garbagae collector has to delete old objects that got out of scope and second, to create new collection objects although there is already one.
As an addition to Der Kommissar's excellent answer.
Starting with .NET 5 (C# 9.0) there are init-only properties. These properties are only settable under specific circumstances, see here for reference.
The following example should not raise a warning CA2227, yet still allow for the collection being set during object initialization.
using System.Collections.Generic;
namespace BookStore
{
public class BookModel
{
public ICollection<string> Chapters { get; init; }
}
}
Note that the current version of the .NET SDK still raises a warning when using the built-in analyzer (not the NuGet package). This is a known bug and should be fixed in the future.
To cover all the possible scenarios to resolve CA2227 error:
This covers the Entity relationship mapping when we use Entity Framework.
class Program
{
static void Main(string[] args)
{
ParentClass obj = new ParentClass();
obj.ChildDetails.Clear();
obj.ChildDetails.AddRange();
obj.LstNames.Clear();
obj.LstNames.AddRange();
}
}
public class ChildClass
{ }
public class ParentClass
{
private readonly ICollection<ChildClass> _ChildClass;
public ParentClass()
{
_ChildClass = new HashSet<ChildClass>();
}
public virtual ICollection<ChildClass> ChildDetails => _ChildClass;
public IList<string> LstNames => new List<string>();
}
Suppose you have a class Person :
public class Person
{
public string Name { get; set;}
public IEnumerable<Role> Roles {get; set;}
}
I should obviously instantiate the Roles in the constructor.
Now, I used to do it with a List like this :
public Person()
{
Roles = new List<Role>();
}
But I discovered this static method in the System.Linq namespace
IEnumerable<T> Enumerable.Empty<T>();
From MSDN:
The Empty(TResult)() method caches an
empty sequence of type TResult. When
the object it returns is enumerated,
it yields no elements.
In some cases, this method is useful
for passing an empty sequence to a
user-defined method that takes an
IEnumerable(T). It can also be used to
generate a neutral element for methods
such as Union. See the Example section
for an example of this use of
So is it better to write the constructor like that? Do you use it? Why? or if not, Why not?
public Person()
{
Roles = Enumerable.Empty<Role>();
}
I think most postings missed the main point. Even if you use an empty array or empty list, those are objects and they are stored in memory. The Garbage Collector has to take care of them. If you are dealing with a high throughput application, it could be a noticeable impact.
Enumerable.Empty does not create an object per call thus putting less load on the GC.
If the code is in low-throughput location, then it boils down to aesthetic considerations though.
I think Enumerable.Empty<T> is better because it is more explicit: your code clearly indicates your intentions. It might also be a bit more efficient, but that's only a secondary advantage.
On the performance front, let's see how Enumerable.Empty<T> is implemented.
It returns EmptyEnumerable<T>.Instance, which is defined as:
internal class EmptyEnumerable<T>
{
public static readonly T[] Instance = new T[0];
}
Static fields on generic types are allocated per generic type parameter. This means that the runtime can lazily create these empty arrays only for the types user code needs, and reuse the instances as many times as needed without adding any pressure on the garbage collector.
To wit:
Debug.Assert(ReferenceEquals(Enumerable.Empty<int>(), Enumerable.Empty<int>()));
Assuming you actually want to populate the Roles property somehow, then encapsulate that by making it's setter private and initialising it to a new list in the constructor:
public class Person
{
public string Name { get; set; }
public IList<Role> Roles { get; private set; }
public Person()
{
Roles = new List<Role>();
}
}
If you really really want to have the public setter, leave Roles with a value of null and avoid the object allocation.
The problem with your approach is that you can't add any items to the collection - I would have a private structure like list and then expose the items as an Enumerable:
public class Person
{
private IList<Role> _roles;
public Person()
{
this._roles = new List<Role>();
}
public string Name { get; set; }
public void AddRole(Role role)
{
//implementation
}
public IEnumerable<Role> Roles
{
get { return this._roles.AsEnumerable(); }
}
}
If you intend some other class to create the list of roles (which I wouldn't recommend) then I wouldn't initialise the enumerable at all in Person.
The typical problem with exposing the private List as an IEnumerable is that the client of your class can mess with it by casting. This code would work:
var p = new Person();
List<Role> roles = p.Roles as List<Role>;
roles.Add(Role.Admin);
You can avoid this by implementing an iterator:
public IEnumerable<Role> Roles {
get {
foreach (var role in mRoles)
yield return role;
}
}