I'm curiuous why Expression-bodied properties doesn't create persisant objects.
public List<string> Pages { get; } = new List<string>();
Does create a persistant isntance of List<string>, just like always.
But
public List<string> Pages => new List<string>();
Somehow this does craete a new instance, but seems to be volatile.
Even when adding a new string to Pages won't add it.
There's no runtime- nor compile-time error, but I think there should be at least a warning.
It took me quite a while to figure it out.
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/statements-expressions-operators/expression-bodied-members
Is a bit odd documented.
Properties noted with { get; } have an internal field that stores the value. However, the second example which is expression-bodied, declares the creation of the list as the getter, which means it will be ran every time you access it, and every time it creates a new list.
As noted in comments, it is the same as get { return new List<string>(); }, to explain it in a different way.
To change this behavior, you need to have an internal field which would store the List instance and the expression-bodied member to return it.
Following the link in the documentation you linked, we have read-only properties:
Starting with C# 6, you can use expression body definition to implement a read-only property. To do that, use the following syntax:
PropertyType PropertyName => expression;
The following example defines a Location class whose read-only Name property is implemented as an expression body definition that returns the value of the private locationName field:
public class Location
{
private string locationName;
public Location(string name)
{
locationName = name;
}
public string Name => locationName;
}
(Emphasis mine)
To rephrase it: when you access the Name property, the value held by locationName is returned. If the value of locationName changes, and you access Name again, you get the new value of locationName.
It's equivalent to:
public string Name
{
get => locationName;
}
Or
public string Name
{
get { return locationName; }
}
I am getting started with c#. I am asked to do an assignement that contains writing a unit test for a setter and checking its output. I don't follow the meaning of testing something very trivial that does not contain any logic. here's the example (SetKeywords() is the method to be tested):
public struct Keyword
{
private string keyword;
private KeywordTypes type;
public Keyword(string keyword, KeywordTypes Type =
KeywordTypes.String)
{
this.keyword = keyword;
this.type = Type;
}
public string GetString()
{
return this.keyword;
}
public KeywordTypes WhichType()
{
return this.type;
}
}
public class ShopParser
{
private Keyword[] keywords = new Keyword[0];
public void **SetKeywords**(Keyword[] tags)
{
keywords = tags;
}
}
public Keyword[] GetKeywords()
{
return this.keywords;
}
public static KeywordPair[] ExtractFromTaG(ShopParser parser, string
serializedInput)
{
var findings = new KeywordPair[0];
foreach (var keyword in parser.GetKeywords())
{
var start = serializedInput.IndexOf(keyword.GetStart());
// Check if keyword is in input string, if not continue
with next keyword.
if (start <= -1) continue;
var end = serializedInput.LastIndexOf(keyword.GetEnd());
// Extract the thing between the tags. Tag excluded
start += keyword.GetStart().Length;
var substring = serializedInput.Substring(start, end -
start);
// Add substring to result list
var tmp = new KeywordPair[findings.Length + 1];
var i = 0;
for (; i < findings.Length; ++i)
{
tmp[i] = findings[i];
}
tmp[i] = new KeywordPair(keyword, substring);
findings = tmp;
}
return findings;
}
}
Lack of complex code does not mean there are no design decisions by the author of the class that should be verified and protected by unit tests. I.e. the fact you picked value type for items in the collection makes some behaviors impossible and some trivial - the test are there to clarify that class implements that design decision properly and protects the behavior of the class in case of future modifications.
Unit tests for setters for properties of a collection type (unlike value type int) are actually non trivial because one must verify that contract of the class is defined and properly supported - does setter make a copy of a collection or reference existing one, does it make deep or shallow copy? Testing each of the cases properly is definitely not a trivial task. (Same to lesser extent applies to all reference type properties, but in non-collection cases expectations of behavior are usually more aligned with default).
So what you want to do before writing the test is to decide the behavior of your collection property - does it make copy at the moment of setting or refers to the original live instance. If collection would be of reference type (not the case in the question) you also need to decide if it takes shallow or deep copy (deep copy is unusual).
After you made the decision it is somewhat trivial to write the test to verify. You add following tests:
is the collection exposed via getter has the same items in the same order as one used to call setter (applies to both copy and reference approaches)
use setter with a collection and modify original collection (in case of an array change items in the collection). Verify that the collection exposed by the getter behaves properly (either matches updated one for live reference or stays the same for copied one)
if using collection of non-immutable reference types verify that modifying individual items behave as expected (either reflects modification for non-deep copy or stays the same)
if collection just refers to original one tests may be shortened to just checking for reference equality between the original and value returned by the getter, but doing so will not document expected behavior and limit ability to modify in the future.
One may need additional test to validate that collection returned as result of the getter behaves as designed by the class author - in particular if modification of the resulting collection are reflected in the class' state or not (getter returning shallow/deep copy of the state or just exposing internal state directly as shown in the question).
Please note that it is discouraged to have setters for collection properties - see CA2227: Collection properties should be read only. So code in the question sort of follows the recommendation but better name like "AddKeywords"/"ReplaceKeywords" would clarify behavior rather than general "set".
How to test?
When you call SetKeywords, it should do something. Right now it sets the internal array keywords. So the question you need to ask yourself is how can you be sure it did that? Well you have a GetKeywords method which returns the internal array so we can use that to conduct our tests as below:
[TestClass]
public class ShopParserTests
{
[TestMethod]
public void SetKeyWords__WhenGivenAnArray__MustSetTheInternalArray()
{
// Arrange
var k1 = new Keyword("One", KeywordTypes.String);
var k2 = new Keyword("Two");
var parser = new ShopParser();
var keys = new Keyword[] { k1, k2 };
// Act
parser.SetKeywords(keys);
// Assert
Keyword[] keysReturned = parser.GetKeywords();
Assert.AreEqual(keysReturned[0].GetString(), k1.GetString());
Assert.AreEqual(keysReturned[0].WhichType(), k1.WhichType());
Assert.AreEqual(keysReturned[1].GetString(), k2.GetString());
Assert.AreEqual(keysReturned[1].WhichType(), k2.WhichType());
// More tests
}
}
Some Suggestions
Keep in mind that you may need to write a lot more tests based on your requirements. For example, what if the user does this:
Keyword[] keysReturned = parser.GetKeywords();
keys[0] = new Keyword();
Do you want to allow that?
Also, in C# your classes can be simplified and take advantage of properties. So your Keyword and ShopParser classes be written like this:
public struct Keyword
{
public Keyword(string keyword, KeywordTypes type =
KeywordTypes.String)
{
this.TheKeyword = keyword;
this.KeyType= type;
}
public string TheKeyword { get; private set; }
public KeywordTypes KeyType { get; private set; }
}
public class ShopParser
{
public void SetKeywords(Keyword[] tags)
{
this.KeyWords = tags;
}
public Keyword[] KeyWords { get; private set; }
}
I have a class declared like this :
public class MyClass
{
public IMyInterface1 Prop1 { get; } = new MyImplementation1();
public IMyInterface2 Prop2 { get; } = new MyImplementation2();
public IMyInterface3 Prop3 { get; } = new MyImplementation3();
//[...]
}
I would like the list of implemented types, using reflection.
I dont have an instance of MyClass, just the type.
Ex:
static void Main(string[] args)
{
var aList = typeof(MyClass).GetProperties(); // [IMyInterface1, IMyInterface2, IMyInterface3]
var whatIWant = GetImplementedProperties(typeof(MyClass)); // [MyImplementation1, MyImplementation2, MyImplementation3]
}
IEnumerable<Type> GetImplementedProperties(Type type)
{
// How can I do that ?
}
PS: I'm not sure the title is well adapted, but I have found nothing better. I am open to suggestions.
Reflection is type metadata introspection, thus, it can't get what an actual instance of a given type may contain in their properties unless you provide an instance of the so-called type.
That's the main reason why reflection methods like PropertyInfo.GetValue have a first mandatory argument: the instance of the type where the property is declared on.
You're in the wrong direction if you want to use reflection for this. Actually you need a syntax analyzer and luckily, C# 6 comes with the new and fancy compiler formerly known as Roslyn (GitHub repository). You can also use NRefactory (GitHub repository).
Both can be used to parse actual C# code. You can parse the whole source code and then get what classes are returned in expression-bodied properties.
You can't get real types without class instance, because properties are initialized only for instances. For instance of the class, you can do something like that
List<Type> propertyTypes = new List<Type>();
PropertyInfo[] properties = typeof(MyClass).GetProperties();
foreach(PropertyInfo propertyInfo in properties)
{
propertyTypes.Add(propertyInfo.GetValue(myClassInstance));
}
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>();
}
I am trying to update public parameters of a known type MyClass from a NameValueCollection (see code below). This is possible in MVC3, the Controller class has an UpdateModel method which does exactly this with the Request.Params. I would like to do this outside of the Controller though.
Any thoughts?
public class MyClass
{
public string MyParam { get; set; }
}
...
var values = new NameValueCollection() { { "MyParam", "any string value for my param" } };
var myInstance = new MyClass();
Update(myInstance, values);
Cheers,
T
You should use reflection to perform this task:
This code is provided without proper validation, and it should be put into a method.
var type = myInstance.GetType();
foreach(var keyValue in values)
{
type.InvokeMember(keyValue.Key,BindingFlags.SetProperty,null,myInstance, new object[]{keyValue.Value});
}
There could be an error in this code, but even if it's the case, the general idea is here.
2 remarks: This code will fail miserably if the property doesn't exist on MyClass, or if the property type cannot be assigned from a string. It would therefore require proper validation (I know, I am repeating myself ).
You could use expression tree to perform the job too, particularly if you are setting a lot of values on the same known type, as expression trees can be compiled.