IDictionary assignment shortcuts compiler feature or language feature? - c#

Through some random object creation today I came across this neat little shortcut for a Dictionary<K, V>. Is the following assignment a compiler shortcut or is it a feature of Dictionary<string, string>.
IDictionary<string, string> items = { { "item1key", "item1value" } };
Looking at the source for Dictionary<K, V> I don't see anything offhand for how this works. Implementing all the interfaces for this class dot not allow me to perform a similar operation. Why is it that we can do it for a dictionary but not another type. For example, how does the compiler or language feature know that the first item is a key and the second item is the value. Or even more specific this same syntax can't be used for a List<string>
List<string> items = { "item1" };
So the first is valid, why?
I'm not necessarily trying to duplicate this but rather curious as to why it is the way it is. What makes a dictionary special in this case?
Example that works
public class Button
{
public string Title { get; set; }
public ButtonType Type { get; set; }
public IDictionary<string, string> Items { get; set; }
public bool RequiresSelected { get; set; }
}
var buttons = new List<Button>
{
new Button {
Items = {
{"button1", "Button 1"},
{"button2", "Button 2"},
{"button3", "Button 3"},
},
Title = "3 Buttons",
Type = ButtonType.DropDown
}
};

The syntax you've shown isn't valid in C#. You'd need:
IDictionary<string, string> items = new Dictionary<string, string>
{ { "item1key", "item1value" } };
At that point it's just a normal collection initializer, so the list equivalent would be:
List<string> items = new List<string> { "item1" };
EDIT: Let's see if my edit can beat yours. My guess is that you've seen something like:
var foo = new Foo {
SomeDictionaryProperty = {
{ "item1key", "item1value" }
}
};
That's an embedded collection initializer, and can be used for lists too. It's not creating a new dictionary, it's adding to an existing one. The code above is equivalent to:
var tmp = new Foo();
tmp.SomeDictionaryProperty.Add("item1key", "item1value");
var foo = tmp;
Another example of it working:
var form = new Form {
Controls = { new Label { Text = "Foo"}, new Label { Text = "Bar" } }
};
See section 7.6.10.2 of the C# 4 specification (Object Initializers) for more information. The important bit is this:
member-initializer:
identifier = initializer-value
initializer-value:
expression
object-or-collection-initializer
So you can initialize a property to either be a specific value (in which case the setter will be used) or via an object/collection initializer, in which case the getter for the property will be used, and then setters or the Add method will be used for the body of the object/collection initializer.

This is a feature of C# compiler, and the dictionary is not special: any collection that supports Add can be initialized in this way. The details are in the section 7.6.10.3 of the C# Language Specification 4.0.
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

C# how to use child method in object type dictionary

I have a dictionary of Lists with types (Weapon, Armour, Potion) that extended from 1 parent (Item), in a child classes GetItemData overrides with different properties
internal abstract class Item
{
protected string name = "Item";
protected int level = GameConstants.MIN_ITEM_LEVEL;
protected string description = "";
protected Types type;
public enum Types
{
armour, potion, weapon
}
public Dictionary<string, dynamic> GetItemData()
{
return new Dictionary<string, dynamic>() {
{ "name", name},
{ "description", description },
{ "type", type }
};
}
}
//dictionary in player class
protected Dictionary<string, object> inventory
= new()
{
{"WEAPON", new List<Weapon>() },
{"AMOUR", new List<Armour>() },
{"POTION", new List<Potion>() }
//All are extended from Item `class Weapon:Item`
};
I need to show some data from each list to user, for this i create a function that takes a itemSelector for dictionary, and Write data from them to console.
But function that i write is not work, its throw
System.InvalidCastException: "Unable to cast object of type 'System.Collections.Generic.List1[Items.Weapon.Weapon]' to type 'System.Collections.Generic.List1[Items.Item]'."
private void ListPlayerItems(string previewText, string itemSelector)
{
Console.WriteLine(previewText);
int itemIndex = 0;
((List<Item>)inventory[itemSelector]).ForEach(item =>
{
Dictionary<string, dynamic> itemData = item.GetItemData();
Console.Write($"{itemIndex}) Название: {itemData["name"]}; Описание:{itemData["description"]}; ");
if (item is Weapon)
{
Console.Write($"Минимальный урон: {itemData["minDamage"]}; Максимальный урон: {itemData["maxDamage"]}\n");
}
itemIndex++;
});
}
The problem is in Type that takes a List<> in implicit operator
((List<Item>)inventory[itemSelector]).ForEach(item =>
As i get it, i need somehow downcast Item to type of picked List, i dont know how to do that, please help me with that, maybe i make it wrong from start and you know better way to do this
C# does not support variance for classes (see this and this), so List<Item> is not List<Weapon> even if Item is base class for Weapon. For this particular use case you can workaround with non-generic IEnumerable, OfType method and ordinary foreach:
foreach(var item in ((IEnumerable)inventory[itemSelector]).OfType<Item>())
{
// ...use item
}
or leverage covariance of IEnumerable<T>:
foreach(var item in ((IEnumerable<Item>)inventory[itemSelector]))
{
// ...use item
}
Also I would recommend to avoid using dynamic and object typed variables or collections whenever it is possible so possibly it is worth to rework your data structures a little bit.

Why does a combination of object and collection initializers use Add method?

The following combination of object and collection initializers does not give compilation error, but it is fundamentally wrong (https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/object-and-collection-initializers#examples), because the Add method will be used in the initialization:
public class Foo
{
public List<string> Bar { get; set; }
}
static void Main()
{
var foo = new Foo
{
Bar =
{
"one",
"two"
}
};
}
So you'll get NullReferenceException. What is the reason for making such an unsafe decision while developing the syntax of the language? Why not to use initialization of a new collection for example?
First, it's not only for combination of object and collection initializers. What you are referring here is called nested collection initializers, and the same rule (or issue by your opinion) applies to nested object initializers. So if you have the following classes:
public class Foo
{
public Bar Bar { get; set; }
}
public class Bar
{
public string Baz { get; set; }
}
and you use the following code
var foo = new Foo
{
Bar = { Baz = "one" }
};
you'll get the same NRE at runtime because no new Bar will be created, but attempt to set Baz property of the Foo.Bar.
In general the syntax for object/collection initializer is
target = source
where the source could be an expression, object initializer or collection initializer. Note that new List<Bar> { … } is not a collection initializer - it's an object creation expression (after all, everything is an object, including collection) combined with collection initializer. And here is the difference - the idea is not to omit the new, but give you a choice to either use creation expression + object/collection initializer or only initializers.
Unfortunately the C# documentation does not explain that concept, but C# specification does that in the Object Initializers section:
A member initializer that specifies an object initializer after the equals sign is a nested object initializer, i.e. an initialization of an embedded object. Instead of assigning a new value to the field or property, the assignments in the nested object initializer are treated as assignments to members of the field or property. Nested object initializers cannot be applied to properties with a value type, or to read-only fields with a value type.
and
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 target field, property or indexer, the elements given in the initializer are added to the collection referenced by the target.
So why is that? First, because it clearly does exactly what you are telling it to do. If you need new, then use new, otherwise it works as assignment (or add for collections).
Other reasons are - the target property could not be settable (already mentioned in other answers). But also it could be non creatable type (e.g. interface, abstract class), and even when it is a concrete class, except it is a struct, how it will decide that it should use new List<Bar> (or new Bar in my example) instead of new MyBarList, if we have
class MyBarList : List<Bar> { }
or new MyBar if we have
class MyBar : Bar { }
As you can see, the compiler cannot make such assumptions, so IMO the language feature is designed to work in the quite clear and logical way. The only confusing part probably is the usage of the = operator for something else, but I guess that was a tradeoff decision - use the same operator = and add new after that if needed.
Take a look at this code and the output of it due to the Debug.WriteLine():
public class Foo
{
public ObservableCollection<string> _bar = new ObservableCollection<string>();
public ObservableCollection<string> Bar
{
get
{
Debug.WriteLine("Bar property getter called");
return _bar;
}
set
{
Debug.WriteLine("Bar allocated");
_bar = value;
}
}
public Foo()
{
_bar.CollectionChanged += _bar_CollectionChanged;
}
private void _bar_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
{
Debug.WriteLine("Item added");
}
}
public MainWindow()
{
Debug.WriteLine("Starting..");
var foo = new Foo
{
Bar =
{
"one",
"two"
}
};
Debug.WriteLine("Ending..");
}
The output is:
Starting..
Bar property getter called
Item added
Bar property getter called
Item added
Ending..
For you questions:
What is the reason for making such an unsafe decision while developing the syntax of the language? Why not to use initialization of a new collection for example?
Answer:
As you can see the intention of the designer of that feature was not to reallocate the collection but rather to help you add items to it more easily considering that you manage your collection allocation by yourself.
Hope this clear things out ;)
Consider the following code:
class Program
{
static void Main()
{
var foo = new Foo
{
Bar =
{
"one",
"two"
}
};
}
}
public class Foo
{
public List<string> Bar { get; set; } = new List<string>();
}
The compiler does not know whether you already created a new list instance within the class constructor (or in another method).
Recall that collection initializer is a series of calls to Add method on an existing collection!
See also:
Custom Collection Initializers
Also note that this initializer applies to a collection that was exposed as a property. Hence the collection initializer is possible as part of the outer object initializer (the Foo object in your example).
However, if it was a simple variable, the compiler would not let you to intialize the collection this way. Here is an example:
List<string> list =
{
"one",
"two"
};
This will throws a compilation error.
As last example, the output of the following code will be: "one, two, three, four, ". I think that now you understand why.
Pay attention to the list static instance, as well as to the private modifier in the "set" of the Bar property, which does not matters because the initializer just calls the Add method, which is accessible even when the Bar "set" is private.
class Program
{
static void Main()
{
var foo1 = new Foo
{
Bar =
{
"one",
"two"
}
};
var foo2 = new Foo
{
Bar =
{
"three",
"four"
}
};
PrintList(foo1.Bar);
}
public static void PrintList(List<string> list)
{
foreach (var item in list)
{
Console.Write(item + ", ");
}
Console.WriteLine();
}
}
public class Foo
{
private static readonly List<string> _bar = new List<string>();
public List<string> Bar { get; private set; } = _bar;
}
I believe the key thing to understand here is that there are two syntactic sugar flavors at play (or at least, there should be):
Object Initialization
Collection Initialization
Take away the List for a moment and look at the field as an object:
public class Foo
{
public object Bar { get; set; }
}
When using Object Initialization, you assign an object (or null):
var foo = new Foo()
{
Bar = new object(); //or Bar = null
}
Now, let's go back to your original example and slap Collection Initialization on top of this. This time around, the compiler realizes this property implements IEnumerable and the array you have provided is of the right type, so it attempts to call the Add method of the interface. It must first go looking for the object, which in your case is null because you haven't initialized it internally. If you debug through this, you will find that the getter gets called and returns null, hence the error.
The correct way of mixing both features then would be for you to assign a new object that you initialize with your values:
var foo = new Foo()
{
Bar = new List<string>(){ "one", "two" }
};
If you debug this version, you will find that the setter is called instead, with the new instance you initialized.
Alternatively, you can initialize your property internally:
public List<string> Bar { get; set; } = new List<string>();
If you debug this version, you will find that the property is first initialized with a value and your version of the code then executes without error (by calling the getter first):
var foo = new Foo()
{
Bar = {"one", "two"}
};
To illustrate the syntactic sugar aspect, Collection Initialization only works within the confines of a constructor calling statement:
List<string> bar = {"one", "two" }; //ERROR: Can only use array initializer expressions to assign to array types. Try using a new expression instead.
List<string> bar = new[] { "one", "two" }; //ERROR: Cannot implicitly convert type 'string[]' to 'System.Collections.Generic.List<string>'
List<string> bar = new List<string>() { "one", "two" }; //This works!
If you wish to allow initialization like in your original example, then the expectation is that the variable will be set to an instance before the Add method can be called. This is true whether you use syntactic sugar or not. I could just as well run into the same error by doing this:
var foo = new Foo();
foo.Bar.Add("one");
So you may want to initialize the variable in order to cover all bases, unless of course a null value has a semantic meaning in your application.

Why does this wrong object initialisation with curly braces even compile? [duplicate]

This question already has an answer here:
Nested object initializer syntax
(1 answer)
Closed 4 years ago.
Whille creating some dummy data for a collection for a WPF/MVVM project, I produced the following wrong code, which compiles fine, but throws an exception at runtime.
There's a nested structure of data objects, which I wrongly instantiate with only the curly braces (looks like writing JavaScript does cause permanent damage to the brain).
using System.Collections.ObjectModel;
namespace testapp
{
class Program
{
static void Main(string[] args)
{
var collection = new ObservableCollection<TopLevelDataObject>();
collection.Add(new TopLevelDataObject{Nested = {Bar = 5}}); // NullReferenceException
}
class TopLevelDataObject
{
public NestedDataObject Nested { get; set; }
public string Foo { get; set; }
}
class NestedDataObject
{
public double Bar { get; set; }
}
}
}
Why does that compile?
If I create an annonymous type, like Nested = new {Bar = 5}, I get the error message during compilation (which thus fails):
Cannot implicitly convert type '<anonymous type: int Bar>' to 'testapp.Program.NestedDataObject'
Why do I not get such an error when ommitting the new operator?
It even gives me a code hint for the property:
My guess would be that {Bar = 5} is simply a code block, which on its own is a valid thing to have.
But why is it valid to assign a code block to anything (in this case, the Nested property)?
Why does that compile?
Because when that code is compiled, it is compiled as just a set of assignment operations. It doesn't all have to be new instances you create.
If you construct a new instance of Nested from the constructor, you can assign a value to Nested.Bar.
Change public NestedDataObject Nested { get; set; } to this to see how it works:
public NestedDataObject Nested { get; } = new NestedDataObject();
(Note you can never assign a value to Nested outside the constructor in the above code!)
Why does that compile?
Because var x = new SomeType{Property = value}; is the same as:
var x = new SomeType();
x.Property = value;
Indeed we can even leave in the () to have var x = new SomeType(){Property = value}; or even var x = new SomeType(argument){Property = value}; combining passing an argument to the constructor and setting a value.
As such you can see that there is always a constructor called, and if you leave out the parentheses that make that explicit its always the nullary (no-argument) constructor.
Meanwhile a type with no explicit constructor always has a public nullary constructor (the "default constructor").
Hence new TopLevelDataObject{Nested = {Bar = 5}} is the same as:
var temp = new TopLevelDataObject();
temp.Nested.Bar = 5; // NRE on temp.Nested
Because TopLevelDataObject could have a constructor that sets `Nestedt then the code you have could work, so it should compile. Of course, because it doesn't have such a constructor it doesn't work.
(Note that initialisers don't operate quite the same with anonymous types, in that case it gets rewritten to call a hidden constructor hence allowing the properties to be read-only even though initialisers cannot be used with read-only properties of non-anonymous types. The syntax allows them to look the same and hence be easily understood as similar but the result is not the same).

IList<IList<T>> to IReadonlyCollection<IReadonlyCollection<T>>

I have a list of string array and I would like to make both collection read-only.
So I have this code:
public XmlPatternTree(IList<string> nodeNames, IList<IList<string>> attributeNames,
IList<IList<string>> attributeValues) : this()
{
NodeNames = new ReadOnlyCollection<string>(nodeNames);
AttributeNames = new ReadOnlyCollection<ReadOnlyCollection<string>>();
AttributeValues = attributeValues;
Depth = NodeNames.Count;
}
My issue is that AttributeNames and AttributeValues assignments causes a compilation error, it seems that I can create a ReadonlyCollection of ReadonlyCollection from a non-readonly collection of non-readonly objects.
Is there something I can do other than looping over all the values and add them in the list ?
Thanks
If you change your type from IList<string> to just List<string>, then this should work:
attributeNames.Select((x) => x.AsReadOnly()).ToList().AsReadOnly();
If you can't modify your method signature (i.e. you have to keep IList<string>), then you can do this:
attributeNames.Select((x) => x.ToList().AsReadOnly()).ToList().AsReadOnly();
If the version of the .net framework is greater then 4.0 the generic version of List<> implements the IReadOnlyCollection<> interface.
If it is more convenient for you, you can change your signature from IList<ILIst<>> to List<List<>> and should work fine.
AttributeNames = attributeNames;
AttributeValues = attributeValues;
Just a note on the covariance of the IReadOnlyList<out T> type (similar to vasil oreshenski's answer).
If you decide to have:
public XmlPatternTree(IReadOnlyList<string> nodeNames,
IReadOnlyList<IReadOnlyList<string>> attributeNames,
IReadOnlyList<IReadOnlyList<string>> attributeValues) : this()
{
NodeNames = nodeNames;
AttributeNames = attributeNames;
AttributeValues = attributeValues;
}
public IReadOnlyList<string> NodeNames { get; private set; }
public IReadOnlyList<IReadOnlyList<string>> AttributeNames { get; private set; }
public IReadOnlyList<IReadOnlyList<string>> AttributeValues { get; private set; }
public int Depth => NodeNames.Count;
in your class, then the covariance mentioned means you can use reference conversions, and not any wrapping inside another class, as in:
var nn = new List<string>();
var an = new List<string[]>();
var av = new List<string[]>();
// populate 'nn', 'an', and 'av'
// the following compiles with no wrapper class:
var tree = new XmlPatternTree(nn, an, av);
Of course, people can cast the interfaces back to the actual types, like List<string[]>, and modify the collections without using reflection, if they guess that the type is really that list of arrays. However, that would be quite malignant, so you could assume it is no problem if only "good" people use your class
PS! What I said and coded above with IReadOnlyList<out T> could just as well have been done with IReadOnlyCollection<out T> since it is covariant ("out") as well. You would just not have the indexer access on the properties (such as var name = tree.AttrbuteNames[idx1][idx2]). But then you could use HashSet<> and similar which are not IReadOnlyList<>.

How to create properties automatically?

I have a "settings" class, which has some properties for usability and to restrict set accessor. It seems easy while i had within ten items, but then their count was increased. I need some way to create these properties automatically, something like that:
foreach(var property in SettingsList)
{
_settings.AddAutoProperty(property);
}
It may have deal with reflection, but i can't get to efficient solution.
The properties definition:
public bool cbNextExcCount
{
get { return (bool)this.GetValueById("cbNextExcCount"); }
}
public bool cbSaveOnChangeExc
{
get { return (bool)this.GetValueById("cbSaveOnChangeExc"); }
}
public bool cbAutoIncrement
{
get { return (bool)this.GetValueById("cbAutoIncrement"); }
}
public bool cbRememberOnExit
{
get { return (bool)this.GetValueById("cbRememberOnExit"); }
}
...etc.
UPDATE
To summ up, i wrote the next code:
public IDictionary<string, object> Properties = new ExpandoObject();
private List<string> SettingsList = new List<string>
{
"cbNextExcCount",
"cbSaveOnChangeExc",
"cbAutoIncrement",
"cbRememberOnExit"
};
public void CreateProperties()
{
foreach (string SettingName in SettingsList)
{
Properties.Add(SettingName, () => this.GetValueById(SettingName));
}
}
But i have an error on () => this.GetValueById("cbNextExcCount")):
argument type 'lambda expression' is not assignable to parameter type 'object'.
I can store Func<bool>, but settings may have other type than bool and if i use Func, it's get a bit more complicate to call.
You can't create auto-properties, but you can use an ExpandoObject.
I'm not sure if this is what you're looking for, because using expandos means using duck typing (i.e. dynamic programming).
ExpandoObject sample:
dynamic expando = new ExpandoObject();
expando.PropertyA = "Hello";
expando.PropertyB = "world!";
An interesting thing about expandos is that ExpandoObject implements IDictionary<string, object>, meaning that you can upcast any expando to this type and iterate over its added properties, which could be great for storing run-time created settings.
UPDATE
I was thinking more about a good solution and if SettingList is a custom class developed by yourself, maybe you can add a property called Custom to SettingList and add there settings that aren't added during design-time.
UPDATE 2
In your case, instead of storing the actual value of something, you could add Func<bool> to ExpandoObject's run-time settings:
IDictionary<string, object> settings = new ExpandoObject();
settings.Add("cbNextExcCount", () => this.GetValueById("cbNextExcCount"));
Actually, I don't know this scope in your code sample, but change this to anything that could be an instance of SettingList or whatever.
Once you've added run-time settings, you can type settings variable to dynamic typing in order to access properties like this:
dynamic allSettings = (dynamic)settings;
bool cbNextExcCount = allSettings.cbNextExcCount();
You can consider Expando Objects in System.Dynamic namespace. This article can be a good start.

Categories

Resources