C# array-like constructor for own class - c#

I want to create a custom class that can be initialized like arrays can,
var myCollection = new MyCollection<string> {"a", "b", "c"}
Is there a syntax for creating a class that can interpret that?
I know how to do a similar thing with class properties, such as
var person = new Person { Name = "Santi", LastName = "Arizti" };
But that is not what I am looking for.
This question might already exist, but I just don't know the name of this feature to effectively search it online.

Your class must
Implement IEnumerable
Have an Add method
That's it.
public class MyCollection<T> : IEnumerable<T>
{
protected readonly List<T> _list = new List<T>();
public void Add(T item)
{
_list.Add(item);
}
IEnumerator IEnumerable.GetEnumerator()
{
return _list.GetEnumerator();
}
public IEnumerator<T> GetEnumerator()
{
return _list.GetEnumerator();
}
}
public class Program
{
public static void Main()
{
var x = new MyCollection<string>
{
"A","B"
};
}
}
Here is a Link to Fiddle

Constructing an object using {...} simply creates it using the default constructor and calls an Add method present on it for each of the arguments (or tuples of arguments if nested {...} is used). It only has to implement IEnumerable.
Your example is essentially equivalent to:
var myCollection = new MyCollection<string>();
myCollection.Add("a");
myCollection.Add("b");
myCollection.Add("c");
Here's the simplest example of such a class that can be "initialized" with anything:
public class TestClass : IEnumerable
{
public void Add<T>(params T[] args)
{
}
IEnumerator IEnumerable.GetEnumerator()
{
throw new NotImplementedException();
}
}

Related

Reference redirection

I had an idea about which I couldn't find any direct syntax. I was wondering if it was possible to overload a reference type so that when it is referenced in a certain way it redirects its reference type into a new one.
I'd like to show an example about this:
public class MyClass
{
public ICollection<int> CollectionProperty { get; private set; }
public MyClass()
{
this.CollectionProperty = new List<int>();
}
}
This is just a simple class, but when MyClass is referenced, for example in a foreach, I'd like it to reference its inner collection like this:
MyClass instance = new MyClass();
foreach(int item in instance)
{
// do stuff
}
So here an item would be an int value of the class's collection's.
It was just something I was curious about, I don't know if it's even possible, maybe with some kind of reference overloading, or I don't know.
Thank you for your answers!
You could implement IEnumerable in order to enable foreach functionality.
public class MyClass<T> : IEnumerable<T>
{
public List<T> Collection { get; set;}
public T this[int index]
{
get { return Collection[index]; }
set { Collection.Insert(index, value); }
}
public IEnumerator<T> GetEnumerator()
{
return Collection.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
public MyClass()
{
Collection = new List<T>();
}
}
public class Program
{
public static void Main()
{
var instance = new MyClass<int>();
instance.Collection.Add(1);
instance.Collection.Add(2);
instance.Collection.Add(3);
foreach(int item in instance)
Console.WriteLine(item);
}
}
Output:
1 2 3

Can't serialize collection of collections

I have this model of data:
public abstract class AbstractCollection
{
}
public abstract class TypedAbstractCollection<T1> : AbstractCollection
{
}
public class MyCollection<T> : TypedAbstractCollection<T>, IEnumerable<T>
{
private readonly List<T> _valueList = new List<T>();
public IEnumerator<T> GetEnumerator()
{
return _valueList.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(T value)
{
_valueList.Add(value);
}
}
[XmlInclude(typeof(MyCollection<string>))]
public class Shallow : IEnumerable<AbstractCollection>
{
private readonly List<AbstractCollection> _listOfCollections = new List<AbstractCollection>();
public IEnumerator<AbstractCollection> GetEnumerator()
{
return _listOfCollections.GetEnumerator();
}
IEnumerator IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
public void Add(AbstractCollection sample)
{
_listOfCollections.Add(sample);
}
}
I use IEnumerable in my collection with Add() function to automatically serialization it as collection, but when I try to serialization it in XML:
Shallow shallow = new Shallow
{
new MyCollection<string>
{
"first",
"second"
}
};
XmlSerializer formatter = new XmlSerializer(shallow.GetType(),
new[] { typeof(OneWayMapper<string, string>) });
using (FileStream fs = new FileStream("data.xml", FileMode.OpenOrCreate))
{
formatter.Serialize(fs, shallow);
}
I've got the strange error without any needed information:
The type 'MyCollection' may not be used in this context
But, if I will use instead of MyCollection class MyItem<T> with typed item value - there won't be any errors.
So it's ok with typed collections, abstract class and so on, but not with collection of collections.
How can I fix that?
And I found a problem. To make it work we have to make AbstractCollection inherit of IEnumerable.
And it's very understandable.

C# Cast List of MyType to List of objects

I'm writing an application where I am required to use Reflection to call a method which has parameters of type MyObject.
Method (List<MyObject> input , out List<MyObject> output,..... );
Using reflection I send the parameter of type Object. How can I cast List<MyObject> to List<object>
var parameters = new Object[] { inputs, outputs, userPrams };
System.Type classType = typeof(MyClass);
object instance = Activator.CreateInstance(classType);
classType.InvokeMember(name, BindingFlags.InvokeMethod | BindingFlags.Instance | BindingFlags.Public,null, instance, parameters);
In the code above both input and output are lists of type MyObject
I tried to Cast them to List of Objects but this doesn't work
x.Outputs = grOutputs as IList<object>
Can anyone help?
Your question is not 100% clear, so I assumed the problem you're facing is the one you put in the title:
Cast List of MyType to List of objects
As #Charles said, IList<T> and List<T> are not variant, so you can't cast IList<DerivedClass> to IList<BaseClass>. You have to create new List<BaseClass>.
There are many ways to do that, I think there are two you should consider:
You can use Cast and ToList, but it will require using System.Linq.
var listOfStrings = new List<string>() { "foo", "bar" };
var listOfObjects = listOfStrings.Cast<object>().ToList();
To avoid that, you can use new List<T>(IEnumerable<T> source) constructor. Because IEnumerable<T> is covariant, you can do following:
var listOfStrings = new List<string>() { "foo", "bar" };
var listOfObjects = new List<object>(listOfString);
You might well want to do something like this:
var dogs = new List<Dog>();
var pets = (List<object>)dogs;
pets.Add(new Cat());
The C# language is heavily invested in you stop mixing cats and dogs like this. It violates the hard guarantee that the list only ever contains dogs. You'll have to do it like this instead:
var dogs = new List<Dog>();
var pets = new List<object>(dogs);
pets.Add(new Cat());
Which is fine, it creates a new list, one that no longer guarantees that it only ever contains dogs since it only promises that the list contains object. Pretty useless, typically, you basically lose all knowledge of what the list contains. Forcing you to write hunt-the-fox code that uses the as operator or Reflection to find the proper animal back. Code that fails to do its job at run-time instead of the compiler telling you that its wrong code at build time, when you're still in the comfortable cubicle cocoon.
Which it did.
IList<T> is not covariant, you would need to create a new list if you wanted IList<object>:
x.Outputs = grOutputs.Cast<object>().ToList();
If I understand your question, you could try something like this :
var objList = myClassList.OfType<object>();
You cannot make your desired cast because, as others have written, collections in c# are not covariant.
You can either create a new list, or introduce a ListWrapper class like so:
public class ListWrapper<TOut, TIn> : IList<TOut> where TIn : class, TOut where TOut : class
{
readonly IList<TIn> list;
public ListWrapper(IList<TIn> list)
{
if (list == null)
throw new NullReferenceException();
this.list = list;
}
#region IList<TOut> Members
public int IndexOf(TOut item)
{
TIn itemIn = item as TIn;
if (itemIn != item)
return -1;
return list.IndexOf(itemIn);
}
public void Insert(int index, TOut item)
{
list.Insert(index, (TIn)item);
}
public void RemoveAt(int index)
{
list.RemoveAt(index);
}
public TOut this[int index]
{
get
{
return list[index];
}
set
{
list[index] = (TIn)value;
}
}
#endregion
#region ICollection<TOut> Members
public void Add(TOut item)
{
list.Add((TIn)item);
}
public void Clear()
{
list.Clear();
}
public bool Contains(TOut item)
{
TIn itemIn = item as TIn;
if (itemIn != item)
return false;
return list.Contains(itemIn);
}
public void CopyTo(TOut[] array, int arrayIndex)
{
foreach (var item in list)
{
array[arrayIndex] = item;
arrayIndex++;
}
}
public int Count
{
get { return list.Count; }
}
public bool IsReadOnly
{
get
{
return list.IsReadOnly;
}
}
public bool Remove(TOut item)
{
return list.Remove(item as TIn);
}
#endregion
#region IEnumerable<TOut> Members
public IEnumerator<TOut> GetEnumerator()
{
foreach (var item in list)
yield return item;
}
#endregion
#region IEnumerable Members
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return GetEnumerator();
}
#endregion
}
You might want to make the wrapper read-only since the setter could throw an exception if the incoming object is not of the inner list's item type.

Read only List<T> view and covariance

I wanted to make a read-only indexable "view" of a list, with the slight twist that the read only view should enumerate to an interface of the list type.
interface IIndexable<out T> : IEnumerable<T>
{
T this[int i] { get; }
int Count { get; }
}
The usage scenario would look something like this.
List<Dog> dogs = new List<Dog>[]{ dog1, dog2 }; // Dog implements IAnimal
IIndexable<IAnimal> animals = dogs.AsIIndexable<Dog, IAnimal>();
IAnimal first = animals[0];
I thought this would be possible given covariance and type constraints, but maybe I'm mistaken here.
My first attempt looks like this, which doesn't work because of the type of the enumerator.
internal class ListIndexable<TC, T> : IIndexable<T> where TC : T
{
private List<TC> list;
public ListIndexable(List<TC> list) { this.list = list; }
public T this[int i] { get { return list[i]; } }
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator(); // Computer says no.
}
}
Why can't I return an IEnumerator<TC> as an IEnumerator<T>? The type is covariant and I have constrained it with TC : T. Would it really be unsafe to return an IEnumerator<TC>, or is it simply a weakness here that the compiler's reasoning ignores the type constraint?
I could solve it by using a custom enumerator that wraps the IEnumerator<TC> and returns its items as T's which is of course perfectly legal, but I'm just curious why the above solution can't work.
internal class ListIndexable<TC, T> : IIndexable<T> where TC : T
{
private List<TC> list;
public ListIndexable(List<TC> list) { this.list = list; }
public T this[int i] { get { return list[i]; } }
public IEnumerator<T> GetEnumerator()
{
return new Enumerator(list.GetEnumerator());
}
class Enumerator : IEnumerator<T>
{
private IEnumerator<TC> inner;
internal Enumerator(IEnumerator<TC> inner) { this.inner = inner; }
public bool MoveNext()
{
return inner.MoveNext();
}
public T Current
{
get { return inner.Current; }
}
// ...
}
}
Your original code does not work because of value types. You cannot do the following:
IEnumerable<int> ints = new[] { 1, 2 };
IEnumerable<object> os = ints;
so the compiler won't accept your definition since it can't guarantee that IEnumerable<TC> is compatible with IEnumerable<T> in general. If you add class constraints to T and TC it compiles:
internal class ListIndexable<TC, T> : IIndexable<T> where TC : class, T where T : class
{
}
However, you can define ListIndexable with only one type parameter instead of two. If you change your definition to:
internal class ListIndexable<T> : IIndexable<T>
{
private List<T> list;
public ListIndexable(List<T> list) { this.list = list; }
public T this[int i] { get { return list[i]; } }
public IEnumerator<T> GetEnumerator()
{
return list.GetEnumerator();
}
}
then you can do:
IIndexable<IAnimal> animals = dogs.AsIIndexable();
where AsIndexable can be defined simply as:
public static ListIndexable<T> AsIndexable<T>(this List<T> list)
{
return new ListIndexable<T>(list);
}
If all you need is to create a read-only collection, you can use ReadOnlyCollection to wrap your list, eg:
var dogs = new List<Dog> {new Dog{Name="A"}, new Dog{Name="B"}, new Dog{Name="C"}};
var ro=new ReadOnlyCollection<Dog>(dogs);
IEnumerable<IAnimal> a = ro;

Elegantly passing lists and objects as params

In C#, one can use the params keyword to specify an arbitrary number of typed parameters to a method:
public void DoStuff(params Foo[] foos) {...}
public void OtherStuff {
DoStuff(foo1);
DoStuff(foo2, foo3);
}
If you already have a list of objects, you can turn it into an array to pass to this method:
DoStuff(fooList.ToArray());
However, is there any elegant way to mix-n-match? That is, to pass in multiple objects and lists of objects and have the results flattened into one list or array for you? Ideally, I would like to be able to call my method like this:
DoStuff(fooList, foo1, foo2, anotherFooList, ...);
As of right now, the only way I know how to do this is to pre-process everything into one list, and I don't know of any way to do this generically.
Edit: To be clear, I'm not married to the params keyword, it's just a related mechanism that helped me explain what I wanted to do. I'm quite happy with any solution that looks clean and flattens everything into a single list.
You could create a class with implict conversions to wrap a single element and a list:
public class ParamsWrapper<T> : IEnumerable<T>
{
private readonly IEnumerable<T> seq;
public ParamsWrapper(IEnumerable<T> seq)
{
this.seq = seq;
}
public static implicit operator ParamsWrapper<T>(T instance)
{
return new ParamsWrapper<T>(new[] { instance });
}
public static implicit operator ParamsWrapper<T>(List<T> seq)
{
return new ParamsWrapper<T>(seq);
}
public IEnumerator<T> GetEnumerator()
{
return this.seq.GetEnumerator();
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
then you can change your DoStuff method to:
private static void DoStuff(params ParamsWrapper<Foo>[] foos)
{
Foo[] all = foos.SelectMany(f => f).ToArray();
//
}
You can use Enumerable.Concat to join multiple lists and items like:
DoStuff(fooList
.Concat(Enumerable.Repeat(foo1,1))
.Concat(Enumerable.Repeat(foo2,1))
.Concat(Enumerable.Repeat(anotherFooList))
.ToArray();
Note: there are likely much more readable ways to achieve whatever you are trying to do. Even passing IEnumerable<Foo> is more readable.
You can't quite do what you're trying to do, but with an extension method, you could get pretty close:
void Main()
{
var singleFoo = new Foo();
var multipleFoos = new[] { new Foo(), new Foo(), new Foo() };
var count = DoStuffWithFoos(singleFoo.Listify(), multipleFoos).Count();
Console.WriteLine("Total Foos: " + count.ToString());
}
public IEnumerable<Foo> DoStuffWithFoos(params IEnumerable<Foo>[] fooLists)
{
return fooLists.SelectMany(fl => fl); // this flattens all your fooLists into
// a single list of Foos
}
public class Foo { }
public static class ExtensionMethods
{
public static IEnumerable<Foo> Listify(this Foo foo)
{
yield return foo;
}
}
you could make separate methods to load the objects into the same collection, not that elegant but it will work, and the logic is really easy to follow, and not very hard to implement.
public class Flattener<T> : IEnumerable<T>
{
private List<T> _collection = new List<T> ( );
public void Add ( params T [ ] list )
{
_collection.AddRange ( list );
}
public void Add ( params IEnumerable<T> [ ] lists )
{
foreach ( var list in lists )
_collection.AddRange ( list );
}
public T Result
{
get
{
return _collection.ToArray();
}
}
public IEnumerator<T> GetEnumerator ( )
{
return _collection.GetEnumerator ( );
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator ( )
{
return GetEnumerator ( );
}
}
Flattener<Foo> foos = new Flattener();
foos.Add(fooList, fooList2, fooList3,...);
foos.Add(foo1,foo2,foo3,...);
DoStuff(foos.Result);

Categories

Resources