I have the following class:
public class ListDemo<T> : IList<T>
{
private List<T> list = new List<T>(); // Internal list
public void Add(T item)
{
// Do your pre-add logic here
list.Add(item); // add to the internal list
// Do your post-add logic here
}
// Implement all IList<T> methods, just passing through to list, such as:
}
Then in my code I have:
AuditList<Entities.SetupCenterCode> _centerCodes = new AuditList<Entities.SetupCenterCode>();
using (Logistics.Data.ConfigurationWCF svc = new Data.ConfigurationWCF())
{
_centerCodes = svc.GetCenterCodes(_entity);
foreach (var item in _centerCodes)
{
item.StartTracking();
}
}
But I get an error here with the conversion because svc.GetCenterCodes(_entity); returns a traditional List<>
How can I solve this, is strange because my custom ListDemo class is for built for generic list.
Any clue?
You can have a private constructor that takes a list and then use the implicit operator to cast directly from list:
public class MyList<T>
{
private List<T> myList;
private MyList(List<T> myList)
{
this.myList = myList;
}
public static implicit operator MyList<T>(List<T> list)
{
return new MyList<T>(list);
}
}
Then, Your rest of the code will work without a change:
List<SomeObject> list = new List<SomeObject>();
MyList<SomeObject> myList = list;
Is there some way that this can work?
class A
{
public virtual string Greet() { return "Hello"; }
}
class B : A
{
public override string Greet() { return "Hola"; }
}
class C : A
{
public override string Greet() { return "Привет"; }
}
class Greeting<T>
{
List<T> list;
public Greeting(List<T> list)
{
this.list = new List<T>();
this.list.AddRange(list);
}
public void Show()
{
foreach (T el in list)
el.Greet(); // 'T' does not contain a definition for 'Greet'
}
}
class Program
{
static void Main(string[] args)
{
List<A> list = new List<A>() { new A(), new B(), new C() };
Greeting<A> g = new Greeting<A>(list);
g.Show();
}
}
I know that in this case I should write List<Shop> list(in Greeting class) and don't use generic class...but I need this implementation to work
You need to add a constraint on T, without it, the compiler has no way of knowing you will only pass objects which derive from A and the method isn't available:
class Greeting<T> where T : A
{
List<T> list;
public Greeting(List<T> list)
{
this.list = new List<T>();
this.list.AddRange(list);
}
public void Show()
{
foreach (T el in list)
el.Greet(); // 'T' does not contain a definition for 'Greet'
}
}
I'm working in a code base which has a lot of first class collections.
In order to ease using these collections with LINQ, there is an extension method per collection that looks like:
public static class CustomCollectionExtensions
{
public static CustomCollection ToCustomCollection(this IEnumerable<CustomItem> enumerable)
{
return new CustomCollection(enumerable);
}
}
With the accompanying constructors:
public class CustomCollection : List<CustomItem>
{
public CustomCollection(IEnumerable<CustomItem> enumerable) : base(enumerable) { }
}
This winds up being a bunch of boilerplate so I attempted to write a generic IEnumerable<U>.To<T>() so that we wouldn't have to keep generating these specific ToXCollection() methods.
I got as far as:
public static class GenericCollectionExtensions
{
public static T To<T, U>(this IEnumerable<U> enumerable) where T : ICollection<U>, new()
{
T collection = new T();
foreach (U u in enumerable)
{
collection.Add(u);
}
return collection;
}
}
Which has to be called like customCollectionInstance.OrderBy(i => i.Property).To<CustomCollection, CustomItem>()
Is there a way to avoid having to specify the CustomItem type so we can instead use customCollectionInstance.OrderBy(i => i.Property).To<CustomCollection>() or is this not something that can be done generically?
Something close to what you want:
public static class GenericCollectionExtensions
{
public sealed class CollectionConverter<TItem>
{
private readonly IEnumerable<TItem> _source;
public CollectionConverter(IEnumerable<TItem> source)
{
_source = source;
}
public TCollection To<TCollection>()
where TCollection : ICollection<TItem>, new()
{
var collection = new TCollection();
foreach(var item in _source)
{
collection.Add(item);
}
return collection;
}
}
public static CollectionConverter<T> Convert<T>(this IEnumerable<T> sequence)
{
return new CollectionConverter<T>(sequence);
}
}
Usage:
customCollectionInstance.OrderBy(i => i.Property).Convert().To<CustomCollection>();
Hopefully this isn't a dupe, couldn't find anything related online
I'm getting a strange compile time error in the following extension method:
public static TCol AddRange<TCol, TItem>(this TCol e, IEnumerable<TItem> values)
where TCol: IEnumerable<TItem>
{
foreach (var cur in e)
{
yield return cur;
}
foreach (var cur in values)
{
yield return cur;
}
}
Error:
The body of 'TestBed.EnumerableExtensions.AddRange(TCol, System.Collections.Generic.IEnumerable)' cannot be an iterator block because 'TCol' is not an iterator interface type
Does this mean that generic constraints are not considered by the compiler when determining if a method qualifies for yield return use?
I use this extension method in a class which defines the collection using a generic parameter. Something like (in addition to a few type cast operators):
public class TestEnum<TCol, TItem>
where TCol : class, ICollection<TItem>, new()
{
TCol _values = default(TCol);
public TestEnum(IEnumerable<TItem> values)
{
_values = (TCol)(new TCol()).AddRange(values);
}
public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
...
}
And in turn, used like (remember I have type cast operators defined):
TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
Originally, my extension method looked like:
public static IEnumerable<TItem> AddRange<TItem>(this IEnumerable<TItem> e, IEnumerable<TItem> values)
{
...
}
Which compiles but results in:
Unhandled Exception: System.InvalidCastException: Unable to cast object of type '<AddRange>d__61[System.String]' to type 'System.Collections.Generic.List1[System.String]'.
Is there an alternative way to do this?
As requested, here's a small sample:
class Program
{
public static void Main()
{
TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
}
}
public class TestEnum<TCol, TItem>
where TCol : class, ICollection<TItem>, new()
{
TCol _values = default(TCol);
public TestEnum(IEnumerable<TItem> values)
{
_values = (TCol)(new TCol()).AddRange(values);
}
public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
public static implicit operator TItem(TestEnum<TCol, TItem> item)
{
return item._values.FirstOrDefault();
}
public static implicit operator TestEnum<TCol, TItem>(TCol values)
{
return new TestEnum<TCol, TItem>(values);
}
}
public static class EnumerableExtensions
{
public static IEnumerable<TItem> AddRange<TItem>(this IEnumerable<TItem> e, IEnumerable<TItem> values)
{
foreach (var cur in e)
{
yield return cur;
}
foreach (var cur in values)
{
yield return cur;
}
}
}
To repro the compile-time exception:
class Program
{
public static void Main()
{
TestEnum<List<string>, string> col = new List<string>() { "Hello", "World" };
string someString = col;
Console.WriteLine(someString);
}
}
public class TestEnum<TCol, TItem>
where TCol : class, ICollection<TItem>, new()
{
TCol _values = default(TCol);
public TestEnum(IEnumerable<TItem> values)
{
_values = (TCol)(new TCol()).AddRange(values);
}
public TestEnum(params TItem[] values) : this(values.AsEnumerable()) { }
public static implicit operator TItem(TestEnum<TCol, TItem> item)
{
return item._values.FirstOrDefault();
}
public static implicit operator TestEnum<TCol, TItem>(TCol values)
{
return new TestEnum<TCol, TItem>(values);
}
}
public static class EnumerableExtensions
{
public static TCol AddRange<TCol, TItem>(this TCol e, IEnumerable<TItem> values)
where TCol : IEnumerable<TItem>
{
foreach (var cur in e)
{
yield return cur;
}
foreach (var cur in values)
{
yield return cur;
}
}
}
Let's simplify:
static T M() where T : IEnumerable<int>
{
yield return 1;
}
Why is this illegal?
For the same reason that this is illegal:
static List<int> M()
{
yield return 1;
}
The compiler only knows how to turn M into a method that returns an IEnumerable<something>. It doesn't know how to turn M into a method that returns anything else.
Your generic type parameter T could be List<int> or any of infinitely many other types that implement IEnumerable<T>. The C# compiler doesn't know how to rewrite the method into one that returns a type it knows nothing about.
Now, regarding your method: what is the function of TCol in the first place? Why not just say:
public static IEnumerable<TItem> AddRange<TItem>(
this IEnumerable<TItem> s1, IEnumerable<TItem> s2)
{
foreach(TItem item in s1) yield return item;
foreach(TItem item in s2) yield return item;
}
Incidentally, this method already exists; it is called "Concat".
I'm not sure what are you trying to accomplish, your method certainly doesn't look like AddRange(), because it doesn't add anything to any collection.
But if you write an iterator block, it will return an IEnumerable<T> (or IEnumerator<T>). The actual run-time type it returns is compiler generated and there is no way to force it to return some specific collection, like List<T>.
From your example, AddRange() simply doesn't return List<T>, which is why you can't cast the result to that type. And there is no way to make iterator block return List<T>.
If you want to create a method that adds something to a collection, it probably means you need to call Add(), not return some other collection from the method:
public static void AddRange<T>(
this ICollection<T> collection, IEnumerable<T> items)
{
foreach (var item in items)
collection.Add(item);
}
In response to your comment to Eric Lippert's answer:
I hoped for a solution that would work against IEnumerable but will settle with one for ICollection.
public static void AddRange<TCol, TItem>(this TCol collection, IEnumerable<TItem> range)
where TCol : ICollection<TItem>
{
var list = collection as List<TItem>;
if (list != null)
{
list.AddRange(range);
return;
}
foreach (var item in range)
collection.Add(item);
}
I defined the method as void to mimic the semantics of List<T>.AddRange().
Lets say I have this class:
class MyList<T>
{
}
What must I do to that class, to make the following possible:
var list = new MyList<int> {1, 2, 3, 4};
Have an Add method and implement IEnumerable.
class MyList<T> : IEnumerable
{
public void Add(T t)
{
}
public IEnumerator GetEnumerator()
{
//...
}
}
public void T()
{
MyList<int> a = new MyList<int>{1,2,3};
}
Implementing ICollection on MyList will let the initalizer syntax work
class MyList<T> : ICollection<T>
{
}
Although the bare minimum would be:
public class myList<T> : IEnumerable<T>
{
public void Add(T val)
{
}
public IEnumerator<T> GetEnumerator()
{
}
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
}
}
ICollection<T> is also good.