Object with readonly hashset assign values at initialization of object [duplicate] - c#

Consider the following code:
public sealed class Order
{
public Order()
{
Items = new List<OrderItem>();
}
public List<OrderItem> Items { get; private set; }
}
public sealed class OrderItem
{
}
and here's Order initialization in another class.
var order = new Order
{
Items =
{
new OrderItem(),
new OrderItem()
}
};
Could you explain why it works? As you see the Order has private set property, so I thought it would be impossible to set its value.

Your statement works because the collection initialization syntax uses the Add() method to add the items to the collection rather than setting the member to a new instance of a collection. Essentially, your code is the equivalent of:
var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());
Which is fine since you only ever use the getter method.

Short answer:
It works thru collection initializer which calls Add to add items
Long answer:
Accodingly C# 3.0 cpesification, object which implement IEnumerable and has appropiate Add method can be initialised thru the Add method.
Items has public get accessor and Items it's a List<T> which implements IEnumerable and has Add. Here's how the compiler sees your code
var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());
Please note, the compiler doesn't use info that the List implements IEnumerable, here's the proof, no exception will be thrown
public sealed class Order
{
public Order()
{
Items = new MyCollection();
}
public MyCollection Items { get; private set; }
}
public sealed class OrderItem
{
}
public class MyCollection : IEnumerable
{
private readonly List<OrderItem> _items = new List<OrderItem>();
public void Add(OrderItem item)
{
_items.Add(item);
}
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
from C# Language Specification
The collection object to which a collection initializer is applied
must be of a type that implements System.Collections.IEnumerable or
a compile-time error occurs. For each specified element in order, the
collection initializer invokes an Add method on the target object with
the expression list of the element initializer as argument list,
applying normal overload resolution for each invocation. Thus, the
collection object must contain an applicable Add method for each
element initializer.

Related

Initialize an IEnumerable<T> to a new List<T>

I have a class like this:
public class ItemList
{
[JsonProperty("items")]
public IEnumerable<Item> Items { get; set; }
}
And I want to initialize empty list, like this:
var newItemList = new ItemList
{
Items = new List<Item>()
};
But the Items remains an IEnumerable and I can't use Add or other methods of a list.
Why not declare it as a list
public class ItemList
{
[JsonProperty("items")]
public List<Item> Items { get; set; }
}
List is IEnumerable by extention, so it would make sense to declare it as a list and have all the methods in place.
Since you already declared is public IEnumerable<Item> Items { get; set; } it will not change.
You can do this:
Items = new List<Item>();
and even
ItemList itemlist = new itemlist();
itemlist.Items = new List<Item>();
((List<Item>)itemlist.Items).Add(new Item());
But ((List<Item>)itemlist.Items).Add(new Item()); is not safe. It will work when you initialize as a list, but it will lead to a runtime exception if you try to cast from some other IEnumerable type.
Having IEnumerable allows you to accomplish one of the SOLID principles that say that you must rely on abstraction rather than on concrete classes. Meaning that you can store whatever implementation of IEnumerable within that property (List, HashSet, etc ...).
You have to convert IEnumerable to List If you want to use the Add method implemented by the List class
var myList = newItemList.ToList<T>();
myList.Add(new T());
where T is your concrete class.
You can use either of the below two options.
You can use List Initializer like below
var itemList = new ItemList() {
Items = new List<Item>() {
new Item() { Name = "One" },
new Item() { Name = "Two" }
}
};
You can initialize the list fully and then assign it to the Items
Much better approach would be to use a Builder pattern which would help in constructing the ItemList as described at https://code-maze.com/fluent-builder-recursive-generics/
Sure you can, just cast it:
(Items as List<Item>).Add(someItem);
It's gonna get pretty boring casting it all the time, perhaps:
public class ItemList
{
private List<Item> _items - new List<Item>;
[JsonProperty("items")]
public IEnumerable<Item> Items { get => _items; private set => _items = value as List<string>; }
}
Then inside the class you can use _items.
I recommend you make that setter private and set the items to be a List internally to the class; the cast is needed on the set, but if someone else passes in an IEnumerable that isn't a List it will cause the items collection to be set to null.
If you're going to make the decision that external classes will see an IEnumerable but you're going to have it be a List, then you shouldn't give anyone using your class the opportunity to change it to something that's not a List..
If you're only ever going to set the list once (never change it for a new instance) e.g. in the constructor, then you can make the property readonly instead

How to cast a 'list of classes' to a 'list of its interface'?

I need to cast a class list to its own interface list.
So I have interface Demo_Interface and two classes based on Demo_Interface ,
Now I create list of classes like List<Test_Class1>
And I have a function with List<Demo_Interface> parameter.
Here's interface :
interface Demo_Interface
{
int test_int { get; set; }
}
Here's Entire Code :
using System;
using System.Collections.Generic;
namespace ConsoleApp3
{
class Program
{
///// Main Interface
interface Demo_Interface
{
int test_int { get; set; }
}
//// Class 1 Based On Demo_Interface
class Test_Class1 : Demo_Interface
{
public int test_int { get; set; }
public string test_string { get; set; }
}
///// Class 2 Based On Demo_Interface
class Test_Class2 : Demo_Interface
{
public int test_int { get; set; }
public string test_string { get; set; }
}
//// And Main Class
class Main_Class
{
public List<Test_Class1> class_list_1 { get; set; }
public List<Test_Class2> class_list_2 { get; set; }
public Main_Class()
{
class_list_1 = new List<Test_Class1>() { };
class_list_2 = new List<Test_Class2>() { };
}
}
//// Console Main
static void Main(string[] args)
{
var new_main_class = new Main_Class();
Output_Class(new_main_class.class_list_1); ///// ==> ERROR
Console.ReadKey();
}
//// Simple Function for do something with interface
static void Output_Class(List<Demo_Interface> inter_input)
{
for (int i = 0; i < inter_input.Count; i++)
{
Console.WriteLine("{0} - {1}",i, inter_input[i].test_int);
}
}
}
}
How Can I cast List<Test_Class1> to List<Demo_Interface> , When Test_Class1 uses Demo_Interface?
You can try
List<Test_Class1> testDemo = new List<Test_Class1>(); //list of Test_Class1 instances
List<Demo_Interface> result = testDemo.ToList<Demo_Interface>();
This is safe because we are not directly casting testDemo to its interface. We are keeping testDemo as it is and we are creating result which is list of Demo_Interface
You cannot cast a List<ClassThatImplementsInterface> as a List<IInterfaceItImplements>.
If you could, and you did this:
var classList = new List<ClassThatImplementsInterface>();
var interfaceList = (List<IInterfaceItImplements>)classList;
... then you would be able to do this:
interfaceList.Add(new SomeOtherClassThatImplementsTheInterface);
But casting the list doesn't create a new list. In the above example there aren't two lists. There are two variables with references to the same list. If you could cast as seen above, you would be able to define a list of one type and add a completely different type to it. The compiler prevents that.
You could
create a new List<IDemoInterface> and add the items to it. (Or an array, IEnumerable, etc.)
Leave the list as-is, and just cast individual items when/if you need to. In most cases we wouldn't need to cast something as an interface it implements.
If we need to cast a whole collection as a different type, it's likely because we're passing it as an argument.
That's actually a good reason not to define a method argument as a collection type like a List<T> which can be modified unless it's our intent to modify the collection.
That's one reason why we pass less-specific collection types, like IEnumerable<T>.
Suppose the method argument looks like this:
void MethodINeedToPassTheArgumentTo(IEnumerable<IDemoInterface> items)
Now we can take our List<TestClass> and do this:
MethodINeedToPassTheArgumentTo(testClassList.Cast<IDemoInterface>);
We're not creating a new collection. We're passing a reference that allows the other method to view the items in the list, each individually cast as IDemoInterface. For practical purposes it looks to the other method like a collection of IDemoInterface, and that's okay because the other item can't modify the collection. It can't attempt to add other types into the List<TestClass>.
If you need only to enumerate through the List<Demo_Interface> like shown in example, you don't have to do any kind of explicit casting. List<T> implements IEnumerable<T> which is covariant generic type.
Covariance for collections enables implicit conversion of a collection of a more derived type to a collection of a less derived type
In your case, List<Test_Class1> implements IEnumerable<Test_Class1>, but since Test_Class1 implements Demo_Interface, you can take advantage of generics variance and write, for example, something like this:
IEnumerable<Test_Class1> col = new List<Test_Class1>();
IEnumerable<Demo_Interface> colImplicit = col;
That basically means that your Output_Class method can take IEnumerable<Demo_Interface> argument and you'll be able to pass both lists without casting them explicitly using Cast<T> or creating a new collection using ToList<T>.
private void Output_Class(IEnumerable<Demo_Interface> inter_input)
{
// do your thing
}
// Method invocation
Output_Class(new_main_class.class_list_1);

c# property with private setter: why do new [..class..] .. { private property initializer = ... } work? [duplicate]

Consider the following code:
public sealed class Order
{
public Order()
{
Items = new List<OrderItem>();
}
public List<OrderItem> Items { get; private set; }
}
public sealed class OrderItem
{
}
and here's Order initialization in another class.
var order = new Order
{
Items =
{
new OrderItem(),
new OrderItem()
}
};
Could you explain why it works? As you see the Order has private set property, so I thought it would be impossible to set its value.
Your statement works because the collection initialization syntax uses the Add() method to add the items to the collection rather than setting the member to a new instance of a collection. Essentially, your code is the equivalent of:
var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());
Which is fine since you only ever use the getter method.
Short answer:
It works thru collection initializer which calls Add to add items
Long answer:
Accodingly C# 3.0 cpesification, object which implement IEnumerable and has appropiate Add method can be initialised thru the Add method.
Items has public get accessor and Items it's a List<T> which implements IEnumerable and has Add. Here's how the compiler sees your code
var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());
Please note, the compiler doesn't use info that the List implements IEnumerable, here's the proof, no exception will be thrown
public sealed class Order
{
public Order()
{
Items = new MyCollection();
}
public MyCollection Items { get; private set; }
}
public sealed class OrderItem
{
}
public class MyCollection : IEnumerable
{
private readonly List<OrderItem> _items = new List<OrderItem>();
public void Add(OrderItem item)
{
_items.Add(item);
}
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
from C# Language Specification
The collection object to which a collection initializer is applied
must be of a type that implements System.Collections.IEnumerable or
a compile-time error occurs. For each specified element in order, the
collection initializer invokes an Add method on the target object with
the expression list of the element initializer as argument list,
applying normal overload resolution for each invocation. Thus, the
collection object must contain an applicable Add method for each
element initializer.

Why can I use a collection initializer with private set access from another class?

Consider the following code:
public sealed class Order
{
public Order()
{
Items = new List<OrderItem>();
}
public List<OrderItem> Items { get; private set; }
}
public sealed class OrderItem
{
}
and here's Order initialization in another class.
var order = new Order
{
Items =
{
new OrderItem(),
new OrderItem()
}
};
Could you explain why it works? As you see the Order has private set property, so I thought it would be impossible to set its value.
Your statement works because the collection initialization syntax uses the Add() method to add the items to the collection rather than setting the member to a new instance of a collection. Essentially, your code is the equivalent of:
var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());
Which is fine since you only ever use the getter method.
Short answer:
It works thru collection initializer which calls Add to add items
Long answer:
Accodingly C# 3.0 cpesification, object which implement IEnumerable and has appropiate Add method can be initialised thru the Add method.
Items has public get accessor and Items it's a List<T> which implements IEnumerable and has Add. Here's how the compiler sees your code
var order = new Order();
order.Items.Add(new OrderItem());
order.Items.Add(new OrderItem());
Please note, the compiler doesn't use info that the List implements IEnumerable, here's the proof, no exception will be thrown
public sealed class Order
{
public Order()
{
Items = new MyCollection();
}
public MyCollection Items { get; private set; }
}
public sealed class OrderItem
{
}
public class MyCollection : IEnumerable
{
private readonly List<OrderItem> _items = new List<OrderItem>();
public void Add(OrderItem item)
{
_items.Add(item);
}
public IEnumerator GetEnumerator()
{
throw new NotImplementedException();
}
}
from C# Language Specification
The collection object to which a collection initializer is applied
must be of a type that implements System.Collections.IEnumerable or
a compile-time error occurs. For each specified element in order, the
collection initializer invokes an Add method on the target object with
the expression list of the element initializer as argument list,
applying normal overload resolution for each invocation. Thus, the
collection object must contain an applicable Add method for each
element initializer.

Interface collection member strange behavior during object initialization

I run into runtime NullReferenceException exception in the following code:
public class Container
{
public IList<string> Items { get; set; }
}
class Program
{
static void Main(string[] args)
{
var container = new Container() { Items = {"Test"} };
}
}
It's logical that compiller couldn't create interaface instance but I got a runtime exception, not a compile time. I was confused even more when I investigated this behavior further:
var container = new Container() { Items = {} }; //Legal, Items is null after initialization
var container = new Container() { Items = { "Test" } }; //Legal, throws exception
container.Items = {}; //Illegal doesn't compile
container.Items = {"Test"}; //Illegal doesn't compile
Is this some kind of bug or I don't understand something?
And I'm using .net framework 4.0
It compiles, because compiler has no knowledge about is the List already initialized somewhere else. You can make it work by adding initialization into a constructor:
public class Container
{
public IList<string> Items { get; set; }
public Container()
{
Items = new List<string>();
}
}
Or changing the property to hide a field, which is initialized when class instance is created:
private IList<string> items = new List<string>();
public IList<string> Items
{
get { return items; }
set { items = value; }
}
Then, var container = new Container() { Items = { "Test" } }; works just fine.
At runtime .Add() method is called for every item in collection initializer group. When property is not initialized with new List<string> it has null value, and that's why NullReferenceException is thrown.
Object and Collection Initializers (C# Programming Guide)
By using a collection initializer you do not have to specify multiple
calls to the Add method of the class in your source code; the compiler
adds the calls.
you didn't initialize the List
var container = new Container() { Items = new List<string>() { "Test" } };
By the way the below is legal as for the compiler there is nothing wrong with it (syntax is correct,etc)
var container = new Container() { Items = {} };
but because the compiler doesn't know that Items list has not been initialized (you are not passing any item in the collection initializator {}) the .Add method won't be called on the List and the run-time won't know that the Items object is null
On the other hand the below is legal for the compiler but it throws an exception at run time because you try to initialize the the list passing an item (it is correct for the compiler for the same reason explained above) so when the run time will call the .Add method behind the scene, it will throw a null reference exception because Items has not been initialized
var container = new Container() { Items = { "Test" } };

Categories

Resources