How to make XML Serialization play nicely with C# indexers (this[])? - c#

How can I XML-serialize the following property:
public class SomeCollection
{
[Xml???]
public SomeClass this[int i]
{
get { ... }
set { ... }
}
}
I have tried using XmlElement, XmlArray and XmlArrayItem, to no avail.

The XmlSerializer doesn't work on Indexers, you would either have to implement IXmlSerializable or (better) use a surrogate property to do the heavy lifting for you.

According to Microsoft, indexer can't be serialize with XMLSerializer
http://msdn.microsoft.com/en-us/library/182eeyhh.aspx
Note: XML serialization does not
convert methods, indexers, private
fields, or read-only properties
(except read-only collections).

You can't serialize the indexer property. The best you can do is expose another property (one that's XML-serializable) that ties into the same collection being used by the indexer.
public class SomeCollection
{
[XmlIgnore]
public SomeClass this[int i]
{
get { ... }
set { ... }
}
[XmlArray(ElementName="someClasses")]
[XmlArrayItem(ElementName="someClass", Type=typeof(SomeClass))]
public List<SomeClass> someClasses
{
get
{
// tie this into the same collection access by the indexer...
}
set
{
// tie this into the same collection access by the indexer...
}
}
}

The easiest way is to implement IEnumerable and the appropriate Add method.
For example, if your indexer utilizes List to store your list, the following code should do the job:
Add IEnumerable to the list of interfaces being implemented.
Initialize _list and implement your indexer.
private List<SomeClass> _list;
...
public override void Add(SomeClass instance)
{
mInstances.Add(instance);
}
public IEnumerator<T> GetEnumerator()
{
foreach (SomeClass instance in _list)
{
yield return instance;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
See XmlSerializer Class which includes the following note:
The XmlSerializer gives special treatment to classes that implement IEnumerable or ICollection. A class that implements IEnumerable must implement a public Add method that takes a single parameter. The Add method's parameter must be of the same type as is returned from the Current property on the value returned from GetEnumerator, or one of that type's bases. A class that implements ICollection (such as CollectionBase) in addition to IEnumerable must have a public Item indexed property (indexer in C#) that takes an integer, and it must have a public Count property of type integer. The parameter to the Add method must be the same type as is returned from the Item property, or one of that type's bases. For classes that implement ICollection, values to be serialized are retrieved from the indexed Item property, not by calling GetEnumerator.

Related

Simple existing implementation of ICollection<T>

Is there a simple implementation of ICollection<T> in .NET framework? I.e. a collection class with ability to add and remove items, but without indexing. Collection<T> definitely does not fit, as it implements IList as well and so elements can be accessed by index.
Exposing Collection<T> or List<T> as ICollection<T> will not work in my case too, because I need to inherit my own class from it, and a class inherited from any other class that implements IList<T> will have indexing as well.
I know it is not a big deal to implement one myself, but just thought it should already exist, searched for but did not found anything similar.
Here's a list of classes that implement ICollection<T> in the System.Collections namespace:
System.Collections.Concurrent.ConcurrentDictionary<TKey, TValue>
System.Collections.Generic.Dictionary<TKey, TValue>
System.Collections.Generic.HashSet<T>
System.Collections.Generic.LinkedList<T>
System.Collections.Generic.List<T>
System.Collections.Generic.SortedDictionary<TKey, TValue>
System.Collections.Generic.SortedList<TKey, TValue>
System.Collections.Generic.SortedSet<T>
System.Collections.ObjectModel.Collection<T>
System.Collections.ObjectModel.ReadOnlyCollection<T>
System.Collections.ObjectModel.ReadOnlyDictionary<TKey, TValue>
System.Collections.ObjectModel.WeakReadOnlyCollection<T>
But all of those implementations add extra functionality, and since you want to inherit from an implementation, but only expose ICollection<T> methods, using any of them is not really an option.
The only choice you have is to implement your own. It's easy enough to do. You just need to wrap a suitable implementation of ICollection<T>. Here's one that uses a List<T> by default, but also allows derived classes to use a specific type of ICollection<T>:
class SimpleCollection<T> : ICollection<T>
{
ICollection<T> _items;
public SimpleCollection() {
// Default to using a List<T>.
_items = new List<T>();
}
protected SimpleCollection(ICollection<T> collection) {
// Let derived classes specify the exact type of ICollection<T> to wrap.
_items = collection;
}
public void Add(T item) {
_items.Add(item);
}
public void Clear() {
_items.Clear();
}
public bool Contains(T item) {
return _items.Contains(item);
}
public void CopyTo(T[] array, int arrayIndex) {
_items.CopyTo(array, arrayIndex);
}
public int Count
{
get { return _items.Count; }
}
public bool IsReadOnly
{
get { return false; }
}
public bool Remove(T item)
{
return _items.Remove(item);
}
public IEnumerator<T> GetEnumerator()
{
return _items.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _items.GetEnumerator();
}
}
This is going beyond what you're after, but if, for example, you wanted unique items to be stored, you could derive from this and provide a HashSet<T> as the collection type to wrap:
class UniqueCollection<T> : SimpleCollection<T>
{
public UniqueCollection() : base(new HashSet<T>()) {}
}
Hashset<T>
should work if you would like it to be unordered with unique values.
See MSDN
As mentioned in the comments, ICollection<T> is an even simpler collection that is unordered and will allow duplicate entries. MSDN
ICollection<string> unordered = new Collection<string>();
Finally, I am not aware of a "simple" native .NET implementation of a collection that has .Add() / .Remove() functionality without exposing an index. So, to answer your question, it looks like for your niche functionality you'll have to roll your own.
Just to give a quick differences:
SortedList
Is the best way to use to by get keys rather index and it is based on binary search. Internally, it uses two lists: IList and IList. It does not use Dictionary. Thus it does not have any Hash values.
SortedDictionary is same as SortedList. However, the difference is internal development. Sorted Dictionary is using B Tree. Thus modifications are fast, and lookups are same as sorted list.
Difference between HashSet and List is HashSet ensures uniqueness. That means if you try to add the value twice, it will ignore that value without giving any error or duplicating the same.
So if you don't want to use index based then you can use SortedList that is inherited from ICollection and then IEnumerable...
Otherwise HashSet is the best option where uniqueness is important.

Implementing IEnumerable with an Array

This is likely a simple syntax question, but I can't figure it out.
Normally, I would do this:
public class OrderBook : IEnumerable<PriceLevel>
{
private readonly List<PriceLevel> PriceLevels = new List<PriceLevel>();
public IEnumerator<PriceLevel> GetEnumerator()
{
return PriceLevels.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return PriceLevels.GetEnumerator();
}
}
But instead of a list, I want to use an array - like this:
public class ArrayOrderBook : IEnumerable<PriceLevel>
{
private PriceLevel[] PriceLevels = new PriceLevel[500];
public IEnumerator<PriceLevel> GetEnumerator()
{
return PriceLevels.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return PriceLevels.GetEnumerator();
}
}
The IEnumerator IEnumerable.GetEnumerator() seems to compile fine - but the public IEnumerator<PriceLevel> says that I need some kind of cast - what is the best way of doing this?
William
Try this:
public class ArrayOrderBook : IEnumerable<PriceLevel>
{
private PriceLevel[] PriceLevels = new PriceLevel[500];
public IEnumerator<PriceLevel> GetEnumerator()
{
return PriceLevels.AsEnumerable().GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return PriceLevels.GetEnumerator();
}
}
As you can see from your own IEnumerable<T> implementation, you need to provide both a generic and non-generic version of the method to fulfill the interface. In order to do this, since the methods have the same signature, one of them needs to be an explicit interface implementation. In the case of List, the generic version is a method in the class and the non-generic version is an explicit interface definition, since the generic version is generally more useful. In the case of an array, it already had the non-generic version as the implementation, and it was adding the generic version of the method in a subsequent version. To avoid the breaking change, the generic version is the explicit interface definition instead.
There are a number of ways of solving this issue. Here are three simple ones.
public IEnumerator<PriceLevel> GetEnumerator()
{
return PriceLevels.AsEnumerable().GetEnumerator();
}
public IEnumerator<PriceLevel> GetEnumerator()
{
IEnumerable<PriceLevel> enumerator = PriceLevels;
return enumerator.GetEnumerator();
}
public IEnumerator<PriceLevel> GetEnumerator()
{
return ((IEnumerable<PriceLevel>)PriceLevels).GetEnumerator()
}
Cast T[] to the corresponding IEnumerable<T>:
public IEnumerator<PriceLevel> GetEnumerator()
{
return ((IEnumerable<PriceLevel>)PriceLevels).GetEnumerator();
}
According to ECMA-335 Partition I, ยง8.9.1, a vector type (single dimension array like T[]) implements IList<T> which implies that it also implements IEnumerable<T>. However the implementation of the methods is explicit, so you'd need to use one of these:
Option 1: Simply use the implicit assignment of arrays to IList<T>.
private IList<PriceLevel> PriceLevels = new PriceLevel[500];
Option 2: Leave the member variable as an array, and use the AsEnumerable extension method. This extension method uses a supported implicit assignment which is preferable to using a direct cast like (IEnumerable<PriceLevel>)PriceLevels.
IEnumerator IEnumerable.GetEnumerator()
{
return PriceLevels.AsEnumerable().GetEnumerator();
}
Items to avoid:
The Cast<T> method introduces an unnecessary type check for each element of your array and should be avoided.
If you need to only include non-null elements from the enumeration, it's OK to use the OfType<T> extension method. Otherwise, this method also introduces an unnecessary type check on each element.

Implement same Generic Interface 2 times with different Generic parameter

I had to implement 2 interface same time with different generic parameter as below. I get confused enough about it. I had no idea which one of them iterate itself in foreach. Now i understand first one is implicitly choosen.
I have tried new BarList().GetEnumerator() but i can not specify type parameter on method level.
Only solution i have found it that casting it to interface like(new BarList() as IEnumerable<string>)
After confusing about it enough. I just wanted to know that this design is not really good idea ? I have to avoid to implement same generic interface one more time ?
class Program
{
static void Main(string[] args)
{
foreach (var item in new BarList())
{
}
}
}
class BarList: IEnumerable<string>, IEnumerable<int>
{
public IEnumerator<int> GetEnumerator()
{
throw new NotImplementedException();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
IEnumerator<string> IEnumerable<string>.GetEnumerator()
{
throw new NotImplementedException();
}
}
Edit:
Let me explain why i am going in this way.
I had to Implement IPagedList<T> interface which is inherited from IList<T>. I wanted to write extension method which convert it to My view model. like below
GetAll().ToPagedList(pageindex);//which is returning IPagedList Then i wanted to use it like below;
GetAll().ToPagedList(pageindex).ToViewModel<T,TViewModel>();
For achieve this I tried to return IPagedList<ViewModel> by that extension method.In that case I have to implement IPagedList 2 times with different parameter. But this strategy made confusing things. This is reason of it.
This seems a bit confusing. Why not make it explicit what is happening by adding the enumerators as properties rather than implementing them on the class. For example,
class ProductCollection
{
public IEnumerable<int> EnumerateTheInts { get { //code to produce enumerator }}
public IEnumerable<string> EnumerateTheStringss { get { //code to produce enumerator }}
}
It isn't always bad to implement an open generic interface twice on an object. For example, IHandle could be implemented by a class which can handle two types of T. However, I would find it confusing to implement IEnumerable twice, because you might not enumerate the type you expect in a for-each or in LINQ. Same reasoning for implementing more than one indexer incidentally. The type of your indexer will determine your result, which I can testify to being extremely confusing!
The compiler is picking the IEnumerator<int> GetEnumerator method by following the rules in 8.8.4 of the C# language specification which first looks for an accessible GetEnumerator() method on the BarList type. The only one of those which is available is the one returning IEnumerator<int>.
If you had made that method use explicit interface implementation as well, then it would have gone onto the later stages of section 8.8.4, which states that if there is more than one type T such that there is an implicit conversion from the expression type (BarList here) to IEnumerable<T> then an error is produced.
I would say this is a confusing design - I would probably add properties or methods to retrieve appropriate "views" on the data.
I'd avoid it. However, it depends on your usage.
It will be okay if you just wanted to pass the instance into a function that expects a IEnumerable<string> parameter explicitely:
you won't have to cast
the function won't even 'see' the other interfaces implemented, so there isn't any confusion.
YMMV
Your current design is confusing. While you have not provided any information about the nature of the collection itself, from the name, I can assume you are supposed to iterate over a bunch of products. Perhaps, you should simply have a class of type Product with a string property and an int property and simply return an IEnumerable<Product> instead.
This way, with LINQ extension methods, you can compose the IEnumerable<T> object you actually mean with:
collection.Select(product => product.IntegerProperty)
collection.Select(product => product.StringProperty)
Of course, you can provide helper methods inside the object as well:
class ProductCollection : IEnumerable<Product> {
public IEnumerable<Product> GetEnumerator() {
// ... return Product objects here.
}
public IEnumerable<int> AsIntegerCollection() {
// yield the integer collection here
}
public IEnumerable<string> AsStringCollection() {
// yield the string collection here
}
}
What are these collections of string and ints? I suppose they mean something in relation with the Product (for example Name, Id, etc...) so I would rather do something like this:
class ProductCollection : IEnumerable<Product>
{
public IEnumerator<Product> GetEnumerator()
{
...
}
public IEnumerator<string> ProductNames // a helper to enumerate product names
{
...
}
public IEnumerator<int> ProductIds // a helper to enumerate product ids
{
...
}
}

How to fix casting error?

When i complile the code
class customer
{
Order getOrder;
public IEnumerable<Order> MyOrders
{
get { return getOrder; }
set{getOrder=value;}
}
}
class Order:IEnumerable<Order>
{
string itemName;
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
public IEnumerator<Order> GetEnumerator()
{
return (IEnumerator)this.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator( )
{
return this.GetEnumerator ();
}
}
I receive
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to 'Order'
How to fix it?
get { return getOrder; }
set{getOrder=value;}
value is of type 'System.Collections.Generic.IEnumerable', getOrder is of type Order. Both are in no way compatible. Maybe you want getOrder (unfortunate naming also) to be IEnumerable, too?
Moreover this looks VERY wrong
Order:IEnumerable<Order>
I guess it should be something like IEnumerable<OrderItem>
The first problem is that you have Order implement IEnumerable, which is fairly strange. You can make this work, but it's odd. Normally, you wouldn't enumerate yourself.
The second problem are here:
public IEnumerator<Order> GetEnumerator()
{
return (IEnumerator)this.GetEnumerator();
}
You need to switch how you're implementing your enumerators. IEnumerator<T> inherits IEnumerator, but you're treating it the other way around. Switch the declarations:
public IEnumerator<Order> GetEnumerator()
{
yield return this; // You need to create an ACTUAL enumerator here!
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator(); // This can call the above, since IEnumerator<Order> is also IEnumerator
}
However, in the code you posted, you never actually implement GetEnumerator to return an enumerator. There are many options for this, but you need to actually create an enumerator (or use "yield return" and let the compiler do it for you, which I did above).
You have made an Order class that is IEnumerable<Order>, that enables you to enumerate the class, but it doesn't create a collection that you can enumerate.
You probably just want to create a colleciton of Order items:
List<Order> orders = new List<Order>();
If you want to create a class that is a collection of Order items you can simply inherit from a list:
public class OrderList : List<Order> {}
Then you create the list like this:
OrderList orders = new OrderList();
I see two issues. Your property MyOrders has a return type of IEnumerable<Order>, when the field it returns is actually of type Order.
Also, I am not sure if I read this right - Order has a base class of type IEnumerable<Order> - doesn't this create some sort of endless loop? An enumerable of type enumerable of type enumarable of type enumerable of type enumarable of type enumerable of type enumarable...
What is class Order ? Is it supposed to represent a single order, or a collection of orders? If it is the latter, derive from a built-in collection class that already has IEnumerable in it (like List<>) than rename it so that it's classname communicates that it is a collection (Orders or OrderCollection) and delete the IEnumerable and IEnumerator implementations (They come for free by deriving from List<> ...
// inheriting from List<Order> means you get Enumerable for free
class Orders: List<Order>
{
string itemName;
public string ItemName
{
get { return itemName; }
set { itemName = value; }
}
}
Regarding implicit casting, In your case you cannot cast because a collection is not the same type as the type of object which is in the collection.
But, in general, you cannot implicitly cast a more general type to a more specific type, because it might not be the more specific type. If something cpmes in as an Animal, you cannot implicitly cast it to Dog, - it might be a Cat. (The other way around is all right You can implicitly cast a Dog to an Animal)
If this was the problem, you would fix it by explicitly casting instead.

.NET: How to check the type within a generic typed class?

How do I get the type of a generic typed class within the class?
An example:
I build a generic typed collection implementing ICollection< T>. Within I have methods like
public void Add(T item){
...
}
public void Add(IEnumerable<T> enumItems){
...
}
How can I ask within the method for the given type T?
The reason for my question is: If object is used as T the collection uses Add(object item) instead of Add(IEnumerable<object> enumItems) even if the parameter is IEnumerable. So in the first case it would add the whole enumerable collection as one object instead of multiple objects of the enumerable collection.
So i need something like
if (T is object) {
// Check for IEnumerable
}
but of course that cannot work in C#. Suggestions?
Thank you very much!
Michael
You can use: typeof(T)
if (typeof(T) == typeof(object) ) {
// Check for IEnumerable
}
Personally, I would side step the issue by renaming the IEnumerable<T> method to AddRange. This avoids such issues, and is consistent with existing APIs such as List<T>.AddRange.
It also keeps things clean when the T you want to add implements IEnumerable<T> (rare, I'll admit).
If you want to use the is operator in a generic class/method you have to limit T to a reference type:
public void MyMethod<T>(T theItem) where T : class
{
if (theItem is IEnumerable) { DoStuff(); }
}

Categories

Resources