I've been using C# for a while, but recently noticed that the behaviour of one of my unit tests changed depending on which variation of collection initialiser expression I used:
var object = new Class { SomeCollection = new List<int> { 1, 2, 3 } };
var object = new Class { SomeCollection = { 1, 2, 3 } };
Up until this point I assumed that the second form was just syntactic sugar and was semantically equivalent to the first form. However, switching between these two forms resulted in my failing unit test passing.
The example code below demonstrates this:
void Main()
{
var foo1 = new Foo { Items = new List<int> { 1, 2, 3} };
var foo2 = new Foo { Items = { 1, 2, 3 } };
foo1.Dump();
foo2.Dump();
}
class Foo
{
public List<int> Items { get; set; }
}
When I run this, the first assignment works fine but the second results in a NullReferenceException.
My gut feeling is that behind the scenes the compiler is treating these two expressions as this:
var foo1 = new Foo();
foo1.Items = new List<int> { 1, 2, 3 };
var foo2 = new Foo();
foo2.Items.Add(1);
foo2.Items.Add(2);
foo2.Items.Add(3);
Is that assumption accurate?
Yes, your assumption is accurate. If an object initializer just has:
{
Property = { ... }
}
rather than
{
Property = expression
}
then the setter for the property isn't used - the getter is used, and then either the Add method is called, or properties are set within the returned value. So:
var foo = new Foo
{
Collection = { 1 }
Property =
{
Value = 1
}
};
is equivalent to:
// Only the *getters* for Collection and Property are called
var foo = new Foo();
foo.Collection.Add(1);
foo.Property.Value = 1;
Compare that with:
var foo = new Foo
{
Collection = new List<int> { 1 },
Property = new Bar { Value = 1 }
};
which is equivalent to:
// The setters for Collection and Property are called
var foo = new Foo();
foo.Collection = new List<int> { 1 };
foo.Property = new Bar { Value = 1 };
Related
I have an object with
public class test
{
public object obj1;
public List<string> items;
}
I have a List<test>. For each of the items i want to create a new object with properties from obj1 and an item. The items list can be null or empty. Is there a way to do in Linq?
List<test> tests = new List<test>();
var ob1=new test{ obj1 = "obj1" };
var ob2=new test{ obj1 = "obj2" };
var ob3=new test{ obj1 = "obj3" };
var ob4=new test{ obj1 = null };
tests.Add(ob1);
tests.Add(ob2);
tests.Add(ob3);
tests.Add(ob4);
var result = tests.Select(e => new NewType
{
name = e.obj1 != null ? e.obj1.ToString() : null
});
foreach (var item in result)
{
Console.WriteLine(item.name);
}
Is this what you are looking for?
Say Test.Obj1 has two properties you want to include in each new item, 'Name' & 'ID', along with an item from the list - try a query like this.
Will generate a list of new (anonymous) objects with (for example) the first element of each Test.Obj1.List
IEnumerable<dynamic> results = tests
.Select(o => new { Name = o.obj1.Name,
ID = o.obj1.Id,
Item = o.items.First() ?? null} )
.ToList();
Console.WriteLine(results.ElementAt(0));
Console.WriteLine(results.ElementAt(1));
Console.WriteLine(results.ElementAt(2));
// { Name = Foo, ID = 1, Item = a }
// { Name = Bar, ID = 2, Item = d }
// { Name = Goo, ID = 3, Item = g }
// Example types below...
public class Obj1
{
public string Name {get; set;}
public int Id {get; set;}
public Obj1(string name, int id)
{
Name = name;
Id = id;
}
}
// Create some instances
Obj1 objA = new MyObject("Foo", 1);
Obj1 objB = new MyObject("Bar", 2);
Obj1 objC = new MyObject("Goo", 3);
// Your test class that contains the custom object, and a list
public class Test
{
public Obj1 obj1;
public List<string> items;
public Test(Obj1 myobject, List<string> myItems)
{
obj1 = myobject;
items = myItems;
}
}
// Make a list of test objects
List<Test> tests = new List<Test>{
new Test(objA, new List<string>{"a", "b", "c"}),
new Test(objB, new List<string>{"d", "e", "f"}),
new Test(objC, new List<string>{"g", "h", "i"})};
For new{}, you can replace with a named type T and constructor as needed and update the results list to be
IEnumerable<T>
Also, you may have a specific criteria for which item in the list you want (or all) so you could add a more interesting query in the constructor rather than .First()
Or to just grab the whole test item adjust the constructor as
new { Obj = o.obj1, Items = o.items}
Hope this helps - there are some great links in this collection: https://learn.microsoft.com/en-us/dotnet/csharp/linq/perform-a-subquery-on-a-grouping-operation
While using an object initializer, I tried to instantiate an ICollection through an array initializer by mistake (I forgot the new ... part). What is quite strange, is that the compiler didn't complain at compile time, but rather I got an NullReferenceException at run time.
Here is some code to summarize the situation:
public class FakeClass
{
public ICollection<string> StringsCollection { get; set; }
public string[] StringsArray { get; set; }
}
FakeClass c = new FakeClass();
c.StringsCollection = { "test" }; // doesn't compile - ok
c.StringsCollection = new string[] { "test" }; // compiles - ok
c.StringsArray = { "test" }; // doesn't compile - ok
c.StringsArray = new string[] { "test" }; // compiles - ok
string[] strings = { "sdfqgrt" }; // compiles - ok
strings = { "sdfqgrt" }; // doesn't compile - ok
FakeClass c2 = new FakeClass
{
StringsCollection = { "rthtj" }, // compiles and throws at run - why?
StringsArray = { "egryjt" } // doesn't compile - ok
};
Of course I understand why this code cannot run, but I'm curious about how the compiler can accept such a thing.
StringsCollection = { "rthtj" }
There nothing wrong with this code. It calls implicit to Add method and add the string to the collection.
The NRE exception it because your property isn't initialised yet.
This will work
public ICollection<string> StringsCollection { get; set; } = new List<string>();
Update to answer on the comment
For this code
FakeClass c = new FakeClass();
c.StringsCollection = new string[] { "test" };
FakeClass c2 = new FakeClass
{
StringsCollection = { "dd" }
};
Look on the IL and pay attention to the different between the first and the second StringCollection
For the setter case you need expression like new string[] {} or new List() for the getter case you use the collection initializer.
I have this domain:
public class ADomainClass
{
public int Id { get; set; }
}
public interface IMyClass : IEnumerable<ADomainClass>
{
}
public class MyClass : IMyClass
{
public IEnumerator<ADomainClass> GetEnumerator()
{
IList<ADomainClass> list = new List<ADomainClass>();
//list = GetData...();
foreach (var item in list)
{
yield return item;
}
}
...
}
and want to build the following test:
[Test]
public void TestSample()
{
//Arrange
IMyClass myclass = Substitute.For<IMyClass>();
IList<ADomainClass> testdata = new List<ADomainClass>()
{
new ADomainClass(){ Id = 1, },
new ADomainClass(){ Id = 2, },
new ADomainClass(){ Id = 3, },
new ADomainClass(){ Id = 4, },
};
int count = 0;
myclass.ReturnsForAnyArgs(testdata); //How to set the "return" value?
//Act
foreach (ADomainClass item in myclass)
{
count++;
}
//Assert
count.Should().Be(testdata.Count);
}
Setting the return value for a method is easy and would look like this:
myclass.GetData().Returns(data);
I can't remember how to set the return value when it is an enumerable class. I have solved this once before, but can't remember where I have used it.
My problem was that NSubstitute stores the state of the enum, so if you use the Enumerator twice in the system under test, it will continue with the second element at the second time. I am using something like the code below as a workaround (using a lambda instead of returning the Enumerator, lines 1 + 2 are for demonstration only). I have only tested this with some Linq and foreach.
var bar = new List<string> { "a", "b", "c" };
var foo = Substitute.For<ICollection<string>>();
foo.GetEnumerator().Returns((ci) => { return bar.GetEnumerator(); });
You just need to tell your substitute to return the enumerator from you test data:
myclass.GetEnumerator().Returns(testdata.GetEnumerator());
I have the following code. Is it possible to use reflection to get rid of the first two parameters since the information can be found in the Action assign (Or Expression), which will always have the form of b.P... = a.P...?
class A { .... }; var a = new A { P1 = .... } // class A and B are totally different clas
class B { .... }; var b = new B { P1 = .... } // But they have some properties with the same names
....
AssignAndDoSth(a.P1, b.P1, () => b.P1 = a.P1);
private void AssignAndDoSth<T>(T curr, T prev, Action assign) // assign can be Expression
{
if (!EqualityComparer<T>.Default.Equals(curr, prev))
{
assign();
Do(prev);
....
}
}
The short answer would be "I strongly advise against it"; in reality, this is actually an instance method of a compiler-generated capture class; so you would need to deconstruct the IL of the target method, and evaluate that IL against the fields of the target instance. Not good. What that actually looks like is something like:
var ctx = new SomeType();
ctx.a = new A { P1 = .... };
ctx.b = new B { P1 = .... };
AssignAndDoSth(a.P1, b.P1, new Action(ctx.SomeMethod));
...
class SomeType {
public A a;
public B b;
public void SomeMethod()
{
b.P1 = a.P1;
}
}
The other approach would be to refactor to an Expression<Action> - but that doesn't change the work involved much - it just presents a more friendly API (relatively speaking).
Either way, all that inspection will have a non-trivial performance cost.
An expression tree may not contain an assignment operator, but can contain operator ==
static void AssignAndDoSomething<T>(T curr, T prev, Expression<Func<bool>> assign)
{
var logExpr = assign.Body as System.Linq.Expressions.BinaryExpression;
//output rid of the first parameter
Console.WriteLine(logExpr.Left);
//output rid of the second parameter
Console.WriteLine(logExpr.Right);
//assign first parameter
Expression.Lambda<Action>(Expression.Assign(logExpr.Left, Expression.Constant(curr))).Compile()();
//assign second parameter
Expression.Lambda<Action>(Expression.Assign(logExpr.Right, Expression.Constant(prev))).Compile()();
}
class A
{
public int P1;
}
class B
{
public int P1;
}
var a = new A();
var b = new B();
AssignAndDoSomething(a.P1, b.P1, () => b.P1 == a.P1);
I want to build my own class in C# that is initialized by multiple parameters with curly bracers, like string[]
string[] x = new string[] {
"string1",
"string2",
"string3"
}
Is it possible?
Edit I am sorry for not making myself clear. I wanted a class that can be initialized in an elegant way, with variable amount of parameters.
To support collection initializer syntax, your class needs to implement IEnumerable and have a public Add method.
Example:
class MyClass : IEnumerable<int>
{
public void Add(int value) { ... }
public IEnumerator<int> GetEnumerator() { ... }
IEnumerator IEnumerable.GetEnumerator() { ... }
}
Usage:
var myClass = new MyClass { 1, 2, 3, 4, 5 };
Alternatively, you can define a constructor that takes a variable number of arguments. You can do this using the params keyword.
Example:
class MyClass
{
public MyClass(params int[] args) { ... }
}
Usage:
var myClass = new MyClass(1, 2, 3, 4, 5);
Your syntax above is for initializing an array, not a class. You can, however, initialize properties in the class through a constructor such as:
MyClass foo = new MyClass()
{
X = 1,
Y = 2
};
This is the same as doing:
MyClass foo = new MyClass();
foo.X = 1;
foo.Y = 2;
Yes, it is! Sine C# 3.0 (i think) any instance can be initialized using list initializers!
Example:
class Foo
{
public int X { get; set; }
public int Y { get; set; }
}
Foo f = new Foo() { X = 10, Y = 20 };
See it here http://msdn.microsoft.com/en-us/library/bb397680.aspx
List<StudentName> students = new List<StudentName>()
{
new StudentName {FirstName="Craig", LastName="Playstead", ID=116},
new StudentName {FirstName="Shu", LastName="Ito", ID=112},
new StudentName {FirstName="Gretchen", LastName="Rivas", ID=113},
new StudentName {FirstName="Rajesh", LastName="Rotti", ID=114}
};