C# object initializers and collections - c#

I'm trying to set up a class so that it's possible to initialize it using an object initializer, but it contains some collections. Ideally I'd like client code to be able to do:
MyClass myObj = new MyClass
{
Name = "Name",
Contents = new[]
{
"Item1",
"Item2"
}
}
However, where Contents needs to be a BindingList<string>. The underlying field stores a readonly reference to this list, and I'd like the setter to essentially do a Clear followed by AddRange to set the contents.
I can't make the Contents property an IEnumerable<string>, because client code wouldn't see the Add method, among many others, at least not without casting it first. I can't make it a BindingList<string> because if I set it, I need to construct a new binding list to pass to it.. this might be possible but I'd rather not introduce the inefficiency of construct a new BindingList<string> solely for the purpose of passing it to the property setter.
The ideal thing to be able to do would be to have the getter return a BindingList<string> and the setter accept IEnumerable<string>, but C# doesn't allow getters/setters on a property to have different types.
Oh, and implicitly casting between BindingList<string> and IEnumerable<string> is a no-no, so I can't do that either (http://blogs.msdn.com/b/peterhal/archive/2005/06/20/430929.aspx).
Is there any way around this?

C# initializer syntax will automatically call the Add method on your property's collection object. That won't call Reset() beforehand of course, but the object is still empty at that point, so it doesn't matter.
Does replacing the list have to use property set syntax? Having a setter replace the content of a collection without actually changing the collection object identity is very unexpected and will likely lead to bugs.

Create a custom collection class that derives from BindingList<string> and add an implicit cast from type string[]

I would recommed encapsulating the BindingList. In this situation go back to the old school way of creating objects so that you aren't creating unnecessary couplings. Favor good OO over language conventions.

Related

Why you cannot declare a field and property as having an anonymous type?

I ran into a problem while doing my job, which is porting software from flash AS3 to .NET/Mono. In AS3 code base I can find many Object declarations that are initialized like this:
private const MAPPING:Object =
{
ssdungf:'flydung',
ssdungt:'flydung',
superfutter:'superfeed'
}
The best option for me would be in C# using anonymous type like this:
var MAPPING = new
{
ssdungf = "flydung",
ssdungt = "flydung",
superfutter = "superfeed"
};
The problem is... well let me quote MSDN (source):
You cannot declare a field, a property, an event, or the return type of a method as having an anonymous type
But they don't say why.
So the question remains: why you cannot declare a field and property as having an anonymous type? Why .NET creators stripped it from that option?
I am getting warning here from SO that my question appears subjective, but I think it is not at all - there need to be objective reason for that.
As for me, I don't see any obstacles for that but somehow it is not supported. As compiler can easily generate the type for field or property of class, in a same manner as it does for local variables.
The option for me was to use dynamic type but unfortunately Mono engine I am using is stripped from that.
Also the option for me is to use object type and using later reflection to find these fields:
private static readonly object MAPPING = new
{
ssdungf = "flydung",
ssdungt = "flydung",
superfutter = "superfeed"
};
But using reflection is this situation is dirty I would say.
I tried to find answer, but I really didn't find any. Here are some SO answers to similar questions, but they don't answer why:
Can a class property/field be of anonymous type in C# 4.0?
Declaring a LIST variable of an anonymous type in C#
How do you declare a Func with an anonymous return type?
Why you cannot declare a field and property as having an anonymous type?
Because C# is statically typed, so any memory location has to be given a type, and declaration does so. With locals we can infer from context if its initialised at the same time as declaration with var but that is a shorthand for a type that is usable even when the type hasn't got a name.
What would a field with an anonymous type, that is to say a statically-bound but indescribable type, mean?
dynamic would indeed be the closest analogy to the code you are porting, but since that isn't available to you, you might consider using an IDictionary<string, object> (which incidentally is how ExpandoObject, which is often used with dynamic to have objects that behave more like javascrpt objects, works behind the scenes). This would be slower and less type-safe than if you created a class for the object needed, but can work.
The problem on an anoynmous property is: how do you get/set it?
Suppose it would work:
class MyClass
{
public MyField = new { TheValue = "Hello World" };
}
Now in your consuming code you´d write code to read the code:
MyClass m = new MyClass();
m.MyField.TheValue = "newValue";
How was this different from having a type for MyField? All you´d get is that you can omit two or three lines of code whilst gaining nothing. But I think you might produce many problems as no-one knows what he can assign to/expect from that member.
Furthermore you can´t do much with an anonymous object, basically you can just set it and read it. There are no methods (except Equalsand GetHashCode inherited from object) that you can call so the opportunities are quite low.
Last but not least an anonymous object is usually used as temporaryily, for example within a Select-statement. When you use it you say: this type is going to be used only within the current specific scope and can be ignored by the entire world as internal implementation-detail. Creating a property of an anonymous type will expose such a detail to the outside. Of course you could argue that the designers could at least allow them for private members, but I guess doing so would bypass the complete concept of accessability for nothing.

2 Generic list assignment

if class type list is there named
Collection<PurchaseOrderDetail> poDetails = new Collection<PurchaseOrderDetail>();
and another list with same type is there named _poH.PODetail
why _poH.PODetail = poDetails.ToList(); generates an error
Cannot implicitly convert type 'System.Collections.Generic.List'
to 'System.Collections.ObjectModel.Collection'
what is the solution for this, any explanation please.
All the reason behind the question is
_poH.PODetail = poDetails;
made poDetails.RemoveAt(Convert.ToInt32(e.RowIndex)); updates as well so I was searching for some thing like _poH.PODetail = poDetails.ToCollection();
According to the error message, _poH.PODetail is of type Collection, so assigning a list to it doesn’t work. But since poDetails is a collection itself, you can just assign it directly:
poH.PODetail = poDetails;
So you don’t actually need to call ToList() on it to convert it to a list.
There is no ToCollection method you could call on enumerables, but you could use the Collection constructor that takes a list to make it wrap that list and create a readonly collection:
new Collection(poDetails.ToList());
The short answer is simply that the ToList<T> extension returns an instance of List<T> class which, although similar, is not the same type as Collection<T>.
Basically this doesn't work for the same reasons you cannot set a string value to an integer variable.
One thing you can do though, is initializing the content of a new collection instance with an IList<T> instance. Therefore, the following should give you exactly what you want:
_poH.PODetail = new Collection(poDetails.ToList());
Also, as poke suggested, you might also want to assign the PODetail property with the poDetails variable itself.
_poH.PODetail = poDetails;
However, you must remember that Collection<T> is a reference type. This means that the objects in your collection won't be "copied" inside _poH.PODetail; instead, both poDetails and _poH.PODetail will be pointing to the exact same collection. Any changes done to one collection will automatically be reflected on the other.

Initialising a BindingList with enum values

I'm trying to initialise a BindingList with the values of an enumeration.
According to MSDN BindingList there is a constructor that accepts an IList as a parameter.
My current code works, but seems rather "clunky":
list = new BindingList<Option>();
foreach (Option o in Enum.GetValues(typeof(Option)))
{
list.Add(o);
}
I tried to use this code instead:
list = new BindingList<Option>(Enum.GetValues(typeof(Option)));
but it gave me an error saying it had invalid arguments, even though the return type of Enum.GetValues is Array, which implements IList.
If that constructor essentially does the same thing I do, I would still prefer using the constructor for readability purposes.
I would love if someone could point me to the right way of using this constructor for future use.
This should work for you:
var list = new BindingList<Option>(Enum.GetValues(typeof(Option)) as IList<Option>);
Edit based on your comment:
Although I have no clue why you why you would want to add or remove from a list of enum values, you could do so as follows:
var list = new List<Option>(Enum.GetValues(typeof(Option)) as IEnumerable<Option>);
/* Add anything you want to 'list' here */
var blist = new BindingList<Option>(list as IList<Option>);
/* blist is not readonly any more, so add or remove whatever you want */
The reason it was readonly is because BindingList is cloning the values from an enum. Considering you can't add or remove values from an enum, it makes perfect sense that the Array from Enum.GetValues(), and subsequently the IList that gets passed into BindingList's constructor is readonly. Because BindingList accepts an IList as the starting values, and not just an IEnumberable source, all of the properties of the IList are also cloned into the BindingList, not just the values themselves.
Hope that clarifies why the list was read-only. Although, you may want to reconsider why you need to add to a list of enum values.

Shouldn't this hashtable be read only?

I have a class that has a getter function for a hashtable called _Parameters.
private Hashtable _Parameters = new Hashtable();
public Hashtable Parameters { get { return _Parameters; } }
_Parameters is not referenced anywhere else in code. Now, since there is no setter function I would think that nothing outside of this class could modify what _Parameters has stored, only read it. However that is not the case. Another class calls this code (where template is an instance of the class mentioned above)
template.Parameters[key] = parameters[key];
This ends up modifying _Parameters. How is this possible? Do setter functions only apply if we are assigning vales with an '='?
No. You're returning a reference, which can be modified. But you can't override the reference itself.
Consider using a ReadOnlyDictionary<TKey, TValue> instead.
Consider reading up on Immutable Objects as well. It should explain the subject to you.
There is a difference between changing the HashTable object or changing the content of the HashTable.
The lack of setter makes sure nobody can:
set the HashTable to NULL
change the reference of the HashTable to another HashTable.
The contents of the HashTable however can be changed.
You can access the methods of the Hashtable, you just can't set it to a new value (such as NULL or a new Hashtable instance).
For example, if I have a List of strings and a Property with a getter but no setter, I can add to the list, remove from the list, call the Clear() method to empty out the list... but I can't do a myList = null (or myList = new List()) because there's no setter.
I'm getting the reference to the object, which allows me to manipulate it, but without a setter I cannot set the reference to a new object.
When you return _Parameters, you're not returning a brand new Hashtable. You're returning another reference to the same one. This one, being a reference to the same object, has the full interface of Hashtable usable on it, including things which modify it. What the lack of a setter does is prevent you from replacing the previous _Parameters with a brand new Hashtable
Having a read only property to a class object (as opposed to a struct) only prevents modifying the value of the property, in this case the object reference. However, when you access the property you get a reference to your internal HashTable object, and you are free to call any public method on the HashTable object returned, including adding and removing entries in the HashTable. What that read only property does is prevent a caller from replacing your HashTable object with a completley different HashTable object.

Can I get the item type from a BindingSource?

I would like to get the Type of item that a BindingSource is hooked up to or configured for. The BindingSource.DataSource property can be set to an object, list, or type. If it is a Type, it obviously does not have a bound item yet, but I would still like to get the Type. For a List, I need the item Type, not the list type.
I currently have a custom list type for business objects that implement an IListItemType interface, that I created to solve this problem a while back. I would now like to get this working in a more generic fashion so that it will work with any list.
I've looked through the API docs for for a good way to do this, but so far I have not had any luck. Am I missing something or is this just something I can not or should not be doing?
I recently ran across the ListBindingHelper class in the framework that has everything I was looking for:
System.Windows.Forms.ListBindingHelper.GetListItemType()
Returns the type of an item contained in a list or collection.
ListBindingHelper.GetListItemProperties()
Returns a PropertyDescriptorCollection that describes the properties of the items contained in the list.
There is no completely generic way to get the "type" of the list. The most common method is to examine the first item, but this can be misleading as you can have objects that are of a more specific type in a collection that is less specific (in other words, the collection might be a List<object>, but the first item might be a string, leading you to guess that it's a List<string>). If you're confident that all of the elements will be the same type (meaning none are more specific than the generic type of the collection or than any of the other objects), then examining the first item is the easiest.
Apart from that, you could examine the list's actual type using GetType and check its interfaces. Chances are that any collection that's strongly typed is going to implement IEnumerable<T>, so you can iterate over its interfaces looking for IEnumerable that's generic, then look at its generic type arguments. It's (more than) a little hokey, but it should work.
TL;DR Version
Try this. Assuming you're using .NET 3.5 and have the list stored in a variable called list:
var listType = list.GetType().GetInterfaces()
.Where(t => t.Name == "IEnumerable" && t.IsGenericType)
.Select(t => t.GetGenericArguments()[0]).FirstOrDefault();
As long as the list implements IEnumerable<T>, this will give you T. If it doesn't, chances are the list type is object anyway.
It's been quite a while since this answer has been on board but just in case anybody is still looking for the answer...
I ran into a similar problem. My scenario was that BindingSource.DataSource would always be bound to an IEnumerable BUT there may not be any items in the list. It turns out that BindingSource has a private instance member called "itemType". This field does just what you're looking for: it shows the element type of a list if the BindingSource is bound to a list, or it shows the type of the object that the BindingSource is bound to, if there is no list.
To access the field value, I used some hacky reflection:
FieldInfo fi =
typeof(BindingSource)
.GetField("itemType", BindingFlags.NonPublic | BindingFlags.Instance);
Type myElementType = fi.GetValue(DataBinder.RestrictedDataBinding) as Type;
Without doing much research, I kind of assume that what it's doing is showing the element type of the innerList, which is why it doesn't matter if the DataSource is a list type or not. Also, I assume this field would accurately show the element type of any kind of list that is supported by the BindingSource (including IQueryables, etc.).
WARNING: I have NOT tested this field much so I don't know if there are cases that would make it not read the correct element type. For example, does the field always get accurately updated when the BindingSource's DataSource property is reset? What if the DataSource property is reset to a list that has a different elementType? In my case, these exceptions and others don't apply but you might want to test them.
Lastly, using reflection to hack into private fields breaks all kinds of oop principles. Keep that in mind. Also, keep in mind that there very well might be a good reason why the itemType field was hidden. If you need to investigate further, the code for the BindingSource class is publicly available.

Categories

Resources