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
Related
List has instance method AsReadOnly:
var list = new List<int>();
var readOnlyList = list.AsReadOnly();
That creates ReadOnlyCollection:
public ReadOnlyCollection<T> AsReadOnly()
{
return new ReadOnlyCollection<T>((IList<T>) this);
}
But array has only static method for this:
var array = new int[] { };
var readOnlyArray = Array.AsReadOnly(array);
Doing the same:
public static ReadOnlyCollection<T> AsReadOnly<T>(T[] array)
{
if (array == null)
throw new ArgumentNullException(nameof (array));
return new ReadOnlyCollection<T>((IList<T>) array);
}
What the reason to declare different API for equal methods?
Array is an abstract class and was created before generics was a thing in .NET. If generics had always been a thing this class would almost certainly have been Array<T> rather than Array.
However, we can't go back and change a fundamental base class. As such, we cannot add an instance method to this class that is strongly typed to the specific type T of the array.
Hence, it's added as a static method instead.
This is just a long comment, not an answer to your question.
It is easy enough to create an extension method that gives you the syntax you want:
static class MyExtensions
{
public static ReadOnlyCollection<TEntry> AsReadOnly<TEntry>(this IList<TEntry> li)
=> new ReadOnlyCollection<TEntry>(li);
}
With that, you can do yourArray.AsReadOnly(). This has the benefit that it also works for all other types that happen to implement IList<>.
If you do not want that, of course you can extend only arrays:
static class MyExtensions
{
public static ReadOnlyCollection<TEntry> AsReadOnly<TEntry>(this TEntry[] arr)
=> Array.AsReadOnly(arr);
}
There is no reason to have both methods.
Array in it's nature is read only, i.e. you can't add an item to it and grow it. That is the reason we have no AsReadOnly method like IList.
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);
}
}
Supposing I create an extension method for IList but this extension is part of a library potentially used across many projects. I do not have the control on how it is called.
Is there a way to prevent an Array to call an IList<T> extension method at compile time? This to avoid any misuse, the caller cannot guess the exact implementation, if the .Add() method would be called or only the indexer for example.
I could not find a possible solution with generic constraint type.
So far the only possibility left would be to restrict the extension method to List<T> directly.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var array = new[]{"Hello"};
array.DummyInsert("World"); // this will crash at run time
}
}
public static class DummyExtension
{
public static T DummyInsert<T>(this IList<T> list, T insertValue)
{
list.Add(insertValue);
return insertValue;
}
}
You can add your extension method to List<T> not on IList<T>
I Agree with Ed Plunkett, use a ReadOnlyCollection<T>. But you can do it like this. It's your foot, you can shoot it if you want.
using System;
using System.Collections.Generic;
public class Program
{
public static void Main()
{
var array = new[]{"Hello"};
var world = array.Insert("World"); // this will crash at run time
Console.WriteLine(array.Length);
}
}
public static class DummyExtension
{
public static T Insert<T>(this IList<T> list, T insertValue)
{
Console.WriteLine("WrongInsert");
list.Add(insertValue);
return insertValue;
}
[Obsolete("If want a compile time exception you can do this too.", true)]
public static T Insert<T>(this T[] list, T insertValue)
{
Console.WriteLine("RightInsert");
return insertValue;
}
}
This prints
RightInsert
1
https://dotnetfiddle.net/i6p1Z5
EDIT:
It was pointed out in the comments below that this won't work if your array has been cast to an IList<T> either explicitly or implicitly. There is nothing wrong with using List<T> here instead of IList<T> unless you are trying to actually extend the IList<T>. In that case extend it in a way that makes sense for all IList<T>. I just wanted to show that yes, what you ask can be done. With great power comes great responsibility.
The run-time issue is because of the fact that the Array is of fixed length hence when you try to insert an element into it you end up with an exception. Instead you can have your own extension method for case Array and handle the insertion accordingly.
public class Program
{
public static void Main()
{
var array = new[] { "Hello" };
array = array.Insert("World");
}
}
public static class DummyExtension
{
public static T Insert<T>(this IList<T> list, T insertValue)
{
list.Add(insertValue);
return insertValue;
}
public static T[] Insert<T>(this T[] list, T insertValue)
{
var destArray = new T[list.Length + 1];
Array.Copy(list, destArray, list.Length);
destArray[destArray.Length - 1] = insertValue;
return destArray;
}
}
Well I agree it may be a crude way, but it will work for your case.
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");
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",
});