Initialising a BindingList with enum values - c#

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.

Related

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.

Object assignment in C#

This is something I encountered while using the C# IList collections
IList<MyClass> foo = new List<MyClass>();
var bar = new List<MyClass>();
foo.AddRange() // doesn't compile
bar.AddRange() // compile
As far as I know, in C# (on the contrary of C++) when we create an object with this syntax, the object type get the right side (assignment) and not the left one (declaration).
Do I miss something here !
EDIT
I still don't get it, even after your answers, foo and bar have the same type !
There's nothing so subtle going on here:
foo has the type IList<MyClass> and IList<T> doesn't have an AddRange method
bar has the type List<MyClass> and List<T> does have an AddRange method.
That's all. It would be just the same in C++.
Update: In the edited-in addition, you're calling GetType(), which gets the run-time type of the object - at compile time the compiler is looking at the static type of the foo and bar variables.
Thinking about Object myObj = "MyString" might make things clearer, because there is a more obvious difference between an 'Object' and a 'String', even though they have the same inheritance relationship as IList and List
The problem is that you are using the interface and IList does not have AddRange but List does have AddRange
If you change it to
List<MyClass> foo = new List<MyClass>();
it will work. as it has this method.
Here is what IList has
You can also do
IEnumerable<MyClass> foo = new List<MyClass>();
And you will find that this constrains it even more
EDIT FOR YOUR EDIT:
They will both be the same type becuase both are still Lists. The difference comes in with the variable that you are using. By using the interface, you are limiting the operations to those that the interface supports. As List implements IList, IList has a subset of the operations that List has.
The underlying object is still a List.
The underlying type of foo is List, but its static type, which the compiler uses to ensure correctness, is IList<T>. Anything that you invoke on foo must be declared as part of the IList<T> type.
Try:
List<MyClass> foo = new List<MyClass>(); // CHANGED TO List<> rather than IList<>
var bar = new List<MyClass>();
foo.AddRange() // doesn't compile
bar.AddRange() // compile
Also the IList declares a behavior and not a concrete type. Please see here
http://msdn.microsoft.com/en-us/library/system.collections.ilist%28v=vs.110%29.aspx
In the a List you can add items and IList type is an already constructed type, which implements this interface.
I have not understood your question, but in this situation C# and C++ act the same way. In your example that to provide that the code would be compiled you could write
IList<MyClass> foo = new List<MyClass>();
var bar = new List<MyClass>();
( (List<MyClass> )foo ).AddRange();
bar.AddRange() // compile
In fact it has the same sense as dynamic_cast in C++.
As the all said IList does not support AddRange and it does not need to support it.
You can:
1 ) ((List<MyClass>)foo).AddRange(anotherList);
2 ) You can also use extension method to AddRange
3 ) There are another workarounds for this problem e.g. Concat etc.
I think also, the interface shall not support every function on the class at some point you need abstraction and this shall be based on interface segregation principle.

Converting List<object> to List<complex> where complex is a user-defined type

So lets say I have a class like
public class Complex { } // this is what I mean when I say List<Complex>
// its just a user-defined class or type if you will
Now let's say I have a method which returns a list that holds objects.
Now let's say I can further guarantee that each of these objects in the list is actually of type Complex (in other words List < Complex >)
I would like the most painless way of casting this list as possible. A one-liner would be ideal, but a few lines would be fine too.
Here is what I tried (but doesn't work - InvalidCastOperation exception):
// Sorry this is so hard to read! Also .Cast<Complex>() doesn't work either :(
return (ComplexCollection) ((List<Complex>) ((List<object>) complexElementsDictionary["ComplexElementCollection"]).OfType<Complex>());
Some specifics about this code snippet:
ComplexCollection inherits List<Complex>
complexElementsDictionary is of type (Dictionary<string, List<object>)
So in plain terms I'm asking a Dictionary<> for its corresponding list of objects. Then I try to cast this list to what its supposed to be, which is a list of Complex.
Note - Please try to ignore the fact that I am casting like this at runtime. I'm deserializing an XML collection (in app.config actually) and this is how I've chosen to do it. There may be a better way, but for now I just want to see if anyone knows a way to do what I'm asking.
Thank you so much! I hope it is clear what I'm asking, and if not I'll update the question.
Breaking up your example code and modifying it slightly to use Cast():
List<object> list = complexElementsDictionary["ComplexElementCollection"];
List<Complex> typedList = list.Cast<Complex>().ToList();
You now have a list of objects of the required type. But what you actually want is an instance of your collection object, and this cannot be obtained by another cast. That is, you can't do this:
ComplexCollection collection = (ComplexCollection)typedList;
because typedList simply isn't an instance of ComplexCollection and there is no conversion operator available to the compiler to convert from List<Complex> to ComplexCollection.
You will need to create your instance and add the Complex objects to it:
ComplexCollection collection = new ComplexCollection();
foreach(Complex c in typedList){
collection.Add(c);
}
Or in just four lines:
ComplexCollection collection = new ComplexCollection();
foreach(Complex c in complexElementsDictionary["ComplexElementCollection"].Cast<>(Complex)){
collection.Add(c);
}
To convert a List<object> to List<Complex> is as simple as:
List<object> objectList = complexElementsDictionary["ComplexElementCollection"];
List<Complex> complexList = objectList.Cast<Complex>().ToList();
However you can't cast a List<Complex> to ComplexCollection just because ComplexCollection inherits from List<Complex>. (the other way around is fine though)
List<T> has a constructor which takes an IEnumerable<T> and adds the elements to a list. So I think what you need is to add this constructor to ComplexCollection:
public ComplexCollection(IEnumerable<Complex> values) : base(values)
{
/* plus any other logic you have */
}
And then your example with the return statement becomes:
return new ComplexCollection(
complexElementsDictionary["ComplexElementCollection"].Cast<Complex>()
);
To convert a list to a new type you can just do something as simple as
(ComplexCollection)(yourList.Select(x => (complexType)x).ToList());
That should work, basically your going through your list of objects in the dictionary and casting each item to the appropriate type, then converting that to a list and then converting your list to the more specific collection type.
I'm afried there's no direct solution. Maybe you could use ConvertAll:
// ol is of type List<Object>
List<Complex> cl = ol.ConvertAll(o=>(Complex)c);

C# object initializers and collections

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.

Selecting objects contained in an ArrayList

I'm working on a project for homework where I have an ArrayList containing 5 strings. I know how to select items of the ArrayList (using an index value) but not how to access the objects strings. Any help would be great. Here is what I've tried to do:
private ArrayList myComponents;
private int listIndex = 0;
myComponents = new ArrayList(); //Arraylist to hold catalog data
equipment = new Equipment(itemName, itemType, itemDetails, itemMaintenance, itemId);
myComponents.Add(equipment);
// class file is called Equipment.cs
// I know normally that equipment without the arraylist this would work:
// equipment.getitemName();
// but combining with the arraylist is being problematic.
You will likely be better off using a List instead of an ArrayList. An ArrayList is not strongly typed which means that you cannot treat the things/objects inside the array like they are "Equipment" but rather only like they are a generic boring object.
List<Equipment> myComponents = new List<Equipment> ();
equipment = new Equipment(itemName, itemType, itemDetails, itemMaintenance, itemId);
myComponents.Add(equipment);
foreach(Equipment eq in myComponents)
{
eq.getItemName();
// do stuff here
}
Let me know if this solves your problem.
An ArrayList doesn't know (or care) what kind of objects are placed in it. It treats everything put into it as an Object. When retrieving objects from the ArrayList you will need to convert the returned Object reference into a reference of the appropriate type before you can access that types properties and methods. There are several ways of doing this:
// this will throw an exception if myComponents[0] is not an instance of Equipement
Equipment eq = (Equipment) myComponents[0];
// this is a test you can to to check the type
if(myComponents[i] is Equipment){
// unlike the cast above, this will not throw and exception, it will set eq to
// null if myComponents[0] is not an instance of Equipement
Equipment eq = myComponents[0] as Equipment;
}
// foreach will do the cast for you like the first example, but since it is a cast
// it will throw an exception if the type is wrong.
foreach(Equipment eq in myComponents){
...
}
That being said, if possible, you really want to be using a generic type. The one that works most like an ArrayList is List. Generics help in a lot of cases to avoid all the casting that makes the ArrayList code painful to write and error prone. The downside is of course that you cannot mix types in a List. A List won't let you put a string in it, while an ArrayList full of Equipment instances will. The particular problem you are trying to solve will determine which makes more sense.
Since all the items in the array list are "objects" on the face of it, but they're actually Equipment objects under the covers, you need a way of going from object to Equipment when retrieving the items from the ArrayList (hint: Cast). Don't want to give it away since this is homework, but that should help....

Categories

Resources