C# ICollection extension - c#

I would like to write a C# extension for generic Array but it's always casting an Error. Here is the code i used to create extendsion for string[] which works well :
public static string[] Add(this string[] list, string s, bool checkUnique = false, bool checkNull = true){
if (checkNull && string.IsNullOrEmpty(s)) return list;
if (checkUnique && list.IndexOf(s) != -1) return list;
ArrayList arr = new ArrayList();
arr.AddRange(list);
arr.Add(s);
return (string[])arr.ToArray(typeof(string));
}
What i really want is do it more generic so it will also works for other types not only string (so i tried to replace all string specifics with generics T) :
public static T[] Add(this T[] list, T item, bool checkUnique = false){
if (checkUnique && list.IndexOf(item) != -1) return list;
ArrayList arr = new ArrayList();
arr.AddRange(list);
arr.Add(item);
return (T[])arr.ToArray(typeof(T));
}
but the code won't compile. It's casting an error "error CS0246: The type or namespace name `T' could not be found. Are you missing a using directive or an assembly reference?"
I already tried another solution around :
public static void AddIfNotExists<T>(this ICollection<T> coll, T item) {
if (!coll.Contains(item))
coll.Add(item);
}
but it's casting another error "error CS0308: The non-generic type `System.Collections.ICollection' cannot be used with the type arguments"
As a side note, I'm using Unity C# (which is compiles against 3.5 I think). Can anyone help me ?

Your last method does not compile because of the missing reference to the System.Collections.Generic namespace. You seem to have included the reference to System.Collections only.

You could just use LINQ and make your method a bit simpler:
public static T[] Add<T>(this T[] list, T item, bool checkUnique = false)
{
var tail = new [] { item, };
var result = checkUnique ? list.Union(tail) : list.Concat(tail);
return result.ToArray();
}

You can change the method signature to this:
public static T[] Add<T>(this T[] list, T item, bool checkUnique = false)
{}
However, there are no generic methods for T[] so list.IndexOf(item) won't compile.

Your last code should work IF you are NOT calling it for string arrays, as arrays have fixed sizes!
The following exampl works for me with your extension method that uses ICollection:
List<string> arr = new List<string>();
arr.AddIfNotExists("a");

Related

How can I create my personal ToList() function for static arrays

I must create ToDynamic() function for all types of static arrays,which returns a new dynamic list consisting of the elements of the array from which the function was called (just like ToList() works), like this:
int [] x = {1,2,3,4};
List<int> arr = x.ToDynamic();
You could just use x.ToList() to do what you want. If you really want to implement this yourself, you could just write a generic extension method like this:
public static class ArrayExtensions
{
public static List<T> ToDynamic<T>(this T[] items)
=> new List<T>(items ?? throw new ArgumentNullException(nameof(items)));
}
The scheme of extension methods writing is
// partial: often we have many extensions on collections (Tree, Aggregations etc.)
// which are implemented in different files
public static partial class EnumerableExtensions {
// IEnumerable<T> - choose argument's type being as much generic and basic as you can
public static List<T> ToDinamic<T>(this IEnumerable<T> source) {
// Validate argument(s)
if (null == source)
throw new ArgumentNullException(nameof(source)); // or return null or empty list
return new List<T>(source);
}
}

List not inheriting an IEnumerable extension method

I have the following code that creates an extension to an IEnumerable<T>:
//http://stackoverflow.com/a/1779135/1180926
public static IEnumerable<T> SkipLast<T>(this IEnumerable<T> source, int n)
{
//.......
}
When I try to apply this to a list:
public static void RemoveTwo(List<string> strList)
{
strList = strList.SkipLast(2);
}
I get the following error:
Cannot implicitly convert type 'System.Collections.Generic.IEnumerable' to
'System.Collections.Generic.List'. An explicit conversion
exists (are you missing a cast?)
But List<T> inherits IEnumerable<T> (src), so shouldn't it also inherit its extension methods?
You have to create a list from the IEnumerable<string>:
public static void RemoveTwo(List<string> strList)
{
strList = strList.SkipLast(2).ToList();
}
Your extension returns IEnumerable<string> which is not a List<string>. However, if you want to modify strList you have to use methods of Lists like Remove that modify the original collection or you have to return the new list.
Instead of SkipLast you should use RemoveAt if you want to change the original collection without returning a new list.
public static void RemoveTwo(List<string> strList)
{
if(strList.Count > 0)
strList.RemoveAt(strList.Count-1);
if(strList.Count > 0)
strList.RemoveAt(strList.Count-1);
}
What you're doing wrong is assigning back to the list the IEnumerable<string> return value. You can't do that, as even though all List<T>s are also IEnumerable<T>, the reverse is not true. What you need to do is add a ToList() at the end of the SkipLast call:
public static List<string> RemoveTwo(List<string> strList)
{
return strList.SkipLast(2).ToList();
}
Your issue lies not in the invocation of the extension method (which works as is), but in the assignment of its IEnumerable<string> return value to a List<string> variable. For the sake of demonstrating, the following code would work compile fine (but do nothing):
public static void RemoveTwo(List<string> strList)
{
strList.SkipLast(2);
}

Generic extension method for an array does not compile

Populating a request object for a web-service, I need to dynamically add items to some arrays.
I hoped to simplify it by implementing an extension method:
public static class ArrayExtensions<T> where T : class
{
public static T[] Extend<T>(T[] originalArray, T addItem)
{
if (addItem == null)
{
throw new ArgumentNullException("addItem");
}
var arr = new[] { addItem };
if (originalArray == null)
{
return arr;
}
return originalArray.Concat(arr).ToArray();
}
}
So that this old code:
if (foo.bazArr == null)
{
foo.bazArr = new[] { baz };
}
else
{
foo.bazArr = new[] { baz }.Concat(foo.bazArr).ToArray(); // (i know this inserts the new item at the beginning, but that's irrelevant, order doesn't matter)
}
could be rewritten as:
foo.bazArr = foo.bazArr.Extend(baz); // won't compile
The error is: 'System.Array' does not contain a definition for 'Extend' and no extension method 'Extend' accepting a first argument of type 'System.Array' could be found (are you missing a using directive or an assembly reference?)
Whereas calling the extension method directly like so:
foo.bazArr = ArrayExtensions<someService.bazType>.Extend(foo.bazArr, baz);
compiles fine.
Why is that so? Why can't the compiler infer the type on its own here, if the array is strongly-typed?
EDIT - correct code below:
public static class ArrayExtensions
{
public static T[] Extend<T>(this T[] originalArray, T addItem) where T : class
{
if (addItem == null)
{
throw new ArgumentNullException("addItem");
}
var arr = new[] { addItem };
if (originalArray == null)
{
return arr;
}
return originalArray.Concat(arr).ToArray(); // although Concat is not recommended for performance reasons, see the accepted answer
}
}
For this popular question, here's another good simple example:
public static class Extns
{
// here's an unbelievably useful array handling extension for games!
public static T AnyOne<T>(this T[] ra) where T:class
{
int k = ra.Length;
int r = Random.Range(0,k);
return ra[r];
// (add your own check, alerts, etc, to this example code)
}
}
and in use ..
someArrayOfSoundEffects.AnyOne().Play();
someArrayOfAnimations.AnyOne().BlendLeft();
winningDisplay.text = successStringsArray.AnyOne() +", " +playerName;
SpawnEnormousRobotAt( possibleSafeLocations.AnyOne() );
and so on. For any array it will give you one random item. Used constantly in games to randomise effects etc. The array can be any type.
Missing this:
public static T[] Extend<T>(this T[] originalArray, T addItem)
Without the this it is not an extension method.
Additional note: extending an array one item at a time is expensive. A List<T> would be far preferable. Check to see if your web-service tools offer lists as an option.
Even with arrays, using Enumerable.Concat is probably overkill here; I would simply measure the two arrays, allocate a new one, and use the CopyTo method of each to write into place in the new array.
use this in defining extension method
public static T[] Extend<T>(this T[] originalArray, T addItem)
you have missed the "this" keyword

C# Generic overloading of List<T> : How would this be done?

The StringBuilder class allows you, in what I consider to be a very intuitive way, to chain method calls to .Append(), .AppendFormat() and some others like so:
StringBuilder sb = new StringBuilder();
sb.Append("first string")
.Append("second string);
The List class' .Add() method, on the other hand, returns void - so chaining calls doesn't work. This, in my opinion and the immortal words of Jayne Cobb "just don' make no kinda sense".
I admit that my understanding of Generics is very basic, but I would like to overload the .Add() method (and others) so that they return the original object, and allow chaining. Any and all assistance will be rewarded with further Firefly quotes.
If you want to keep the same name for the Add method, you could hide the method from the base class:
public class MyList<T> : List<T>
{
public new MyList<T> Add(T item)
{
base.Add(item);
return this;
}
}
However, this will only work if you're manipulating the list with a variable explicitly typed as MyList<T> (i.e. it won't work if your variable is declared as IList<T> for instance). So I think the solutions involving an extension method are better, even if that means changing the name of the method.
Although others have already posted solutions with extension methods, here's another one, that has the advantage of conserving the actual type of the collection:
public static class ExtensionMethods
{
public static TCollection Append<TCollection, TItem>(this TCollection collection, TItem item)
where TCollection : ICollection<TItem>
{
collection.Add(item);
return collection;
}
}
Use it like that:
var list = new List<string>();
list.Append("Hello").Append("World");
use can create extension method
public static class ListExtensions
{
public static List<T> AddItem<T>(this List<T> self, T item)
{
self.Add(item);
return self;
}
}
var l = new List<int>();
l.AddItem(1).AddItem(2);
EDIT
we can also make this method generic over collection parameter
public static class ListExtensions
{
public static TC AddItem<TC, T>(this TC self, T item)
where TC : ICollection<T>
{
self.Add(item);
return self;
}
}
var c1 = new Collection<int>();
c1.AddItem(1).AddItem(2);
var c2 = new List<int>();
c2.AddItem(10).AddItem(20);
EDIT 2:
Maybe someone will find this trick useful, it is possible to utilize nested object initializer and collection initializer for setting properties and adding values into existing instances.
using System;
using System.Collections.Generic;
using System.Linq;
struct I<T>
{
public readonly T V;
public I(T v)
{
V = v;
}
}
class Obj
{
public int A { get; set; }
public string B { get; set; }
public override string ToString()
{
return string.Format("A={0}, B={1}", A, B);
}
}
class Program
{
static void Main()
{
var list = new List<int> { 100 };
new I<List<int>>(list)
{
V = { 1, 2, 3, 4, 5, 6 }
};
Console.WriteLine(string.Join(" ", list.Select(x => x.ToString()).ToArray())); // 100 1 2 3 4 5 6
var obj = new Obj { A = 10, B = "!!!" };
Console.WriteLine(obj); // A=10, B=!!!
new I<Obj>(obj)
{
V = { B = "Changed!" }
};
Console.WriteLine(obj); // A=10, B=Changed!
}
}
public static IList<T> Anything-not-Add*<T>(this IList<T> list, T item)
{
list.Add(item);
return list;
}
* AddItem, Append, AppendList, etc. (see comments below)
The same idea came to my mind like other guys' too, independently:
public static TList Anything<TList, TItem>(this TList list, TItem item)
where TList : IList<TItem>
{
list.Add(item);
return list;
}
And Thomas is right: as far as IList<T> inherits ICollection<T> you should use ICollection.
Have an extension method off:
public static List<T> Append(this List<T> list, T item)
{
list.Add(item);
return self;
}
Note that we have to create it with a new name, as if an instance member matches the signature (the 'Add' you are already complaining about) then the extension method won't be called.
In all though, I'd recommend against this. While I like chaining myself, it's being rare in C# libraries means it's not as idiomatic as it is in other languages where it's more common (no technical reason for this, though some differences in how properties work encourages it a bit more in some other languages, just the way things are in terms of what is common). Because of this, the constructs it enables aren't as familiar in C# as elsewhere, and your code is more likely to be misread by another dev.
You could use an extension method with a different name:
public static T Put<T, U>(this T collection, U item) where T : ICollection<U> {
collection.Add(item);
return collection;
}
To create code like this:
var list = new List<int>();
list.Put(1).Put(2).Put(3);
To retain the name Add, however, you can have a method like this:
public static T Add<T, U>(this T collection, Func<U> itemProducer)
where T : ICollection<U> {
collection.Add(itemProducer());
return collection;
}
And create code like this:
list.Add(()=>1).Add(()=>2).Add(()=>3);
It doesn't look that good though.
Maybe if we change the type we can have a better syntax.
Given this class:
public class ListBuilder<T> {
IList<T> _list;
public ListBuilder(IList<T> list) {
_list = list;
}
public ListBuilder<T> Add(T item) {
_list.Add(item);
return this;
}
}
You can have this method:
public static ListBuilder<T> Edit<T>(this IList<T> list) {
return new ListBuilder<T>(list);
}
And use code like this:
list.Edit().Add(1).Add(2).Add(3);
I'm sure you won't appreciate this answer but there's a very good reason that List<>.Add() works this way. It is very fast, it needs to be to be competitive with an array and because it is such a low-level method. It is however just a hair too big to get inlined by the JIT optimizer. It cannot optimize the return statement you'd need to return the list reference.
Writing lst.Add(obj) in your code is for free, the lst reference is available in a CPU register.
A version of Add() that returns the reference makes the code almost 5% slower. It's a lot worse for the proposed extension method, there an entire extra stack frame involved.
I like the extension approach that others have mentioned as that seems to answer the question well (although you would have to give it a different method signature than the existing Add()). Also, it does seem like there's some inconsistency about object returns on calls like this (I thought it was a mutability issue, but the stringbuilder is mutable isn't it?), so you raise an interesting question.
I'm curious, though, if the AddRange method would not work as an out-of-the-box solution? Is there a particular reason you want to chain the commands instead of passing everything in as a an array?
Would do something like this not accomplish what you need?
List<string> list = new List<string>();
list.AddRange(new string[]{
"first string",
"second string",
});

Generic method for converting string array into list

I would like to create a function that will return list of type that is specified by me at run time.
I tried something along this line:
public static List<T> GetMyList<T>(string[] itemList)
{
List<T> resultList = new List<T>(itemList.Length);
return resultList.AddRange(itemList);
}
But this doesn't work. Obviously I don't fully understand how to pass a type to be converted to.
Any help would be appreciated it.
Edit:
It looks like that it is not possible, but here is more info. String array will contain numbers and I would like to convert those numbers sometimes into int, sometimes into short.
Idea behind is to have a generic function that will attempt to convert items into whatever type list I tell it.
You need to provide a method to convert a string into a T - you can do this using a Func<string, T>:
public static List<T> GetMyList<T>(string[] itemList, Func<string, T> conversionFunc)
{
return itemList.Select(conversionFunc).ToList();
}
e.g.
List<int> ints = GetMyList(new[] { "1", "2", "3" }, s => int.Parse(s));
A slightly more elegant solution would be to add an extension method to string that automatically calls the parser for type T, like so:
public static class GenericParser {
public static T Parse<T>(this string input) {
var converter = TypeDescriptor.GetConverter(typeof(T));
if ( converter != null ) {
return ( T )converter.ConvertFromString(input);
}
return default(T);
}
}
Then, your conversion function would look something like:
public static List<T> GetMyList<T>(string[] itemList) {
List<T> list = new List<T>();
list.AddRange(Array.ConvertAll<string, T>(itemList, delegate(string s) {
return s.Parse<T>();
}));
return list;
}
And the usage would be:
List<int> integers = GetMyList<int>(new []{"1", "2", "3", "4"});
List<double> doubles = GetMyList<double>(new []{"1.0", "2.0", "3.0", "4.0"});
and so on.
My first thought is that this won't work because not every object type can be constructed from a string. Perhaps you want something with a signature more like:
public static List<T> GetMyList<T>(T[] itemList)
{
List resultList = new List(itemList.Length);
foreach (t in itemList)
{
resultList.add(t);
}
return resultList;
}
(forgive my syntax. I don't have a compiler handy right now to check it.)
this doesn't work because system has no idea how to convert string to generic T. Also even if it is known, it will not work, because C# (prior to 4) doesn't have type covariance. So use either foreach to copy and convert elements one by one or use Select from Linq
Similar to Lee's but more generic...
public static class Tools
{
public static List<TResult> ToList<T, TResult>(
this IEnumerable<T> input,
Func<T, TResult> conversion)
{
return input.Select(conversion).ToList();
}
public static List<TResult> ToList<T, TResult>(
this IEnumerable<T> input,
Func<T, int, TResult> conversion)
{
return input.Select(conversion).ToList();
}
}
class Program
{
static void Main(string[] args)
{
var input = new[] { "1", "2", "3" };
var ret = input.ToList(i => int.Parse(i));
// 1,2,3
var ret2 = input.ToList((i,j) => int.Parse(i) + j * 10);
// 1,12,23
}
}

Categories

Resources