First of all i have some legacy .NET 2 code and i need to transform a extension function to a simple function. So i will start from what i want to achieve.
I have two object arrays which can be any object like int for example
int[] a = new int[] { 1, 2, 3 };
int[] b = new int[] { 4, 5, 6 };
And the only thing i want to do is this:
var y = Concat(a, b);
and then return an array.
The problem is i can't figure out the correct combination in order to transform the following function.
public static class GenericExt
{
public static T Concat<T>(this T[] args, T[] args2)
{
if (!typeof(T).IsArray) throw new ArgumentException("Concat accepts only arrays");
Type elementType = typeof(T).GetElementType();
Array array = Array.CreateInstance(elementType, args.Length + args2.Length);
args.CopyTo(array, 0);
args2.CopyTo(array, args.Length);
return (T)(object)array;
}
}
Any corrections and help are appreciated.
And the only thing i want to do is this: var y = Concat(a, b); and then return an array.
You can't do that unless you're within the same class as the method (or a subclass). You can stop it being an extension method just by removing the this part from the parameter list - but the call would have to be:
var y = GenericExt.Concat(a, b);
Another alternative would be to supply your own ExtensionAttribute class (in the right namespace), and just keep using it as an extension method. The only thing in the framework that is needed for extension methods is that attribute, and you can just copy the declaration out of MSDN. Note that it will then cause problems if you refer to the library from .NET 3.5+ libraries/applications, where the attribute is part of the framework... but if everything is .NET 2, then you should be fine.
Note that the implementation is broken at the moment - at execution time, you're only accepting type arguments which are also arrays... so you'd actually need to pass in an int[][] (with T being int[]) for it to work. You probably just want:
public static T[] Concat<T>(T[] args, T[] args2)
{
T[] array = new T[args.Length + args2.Length];
args.CopyTo(array, 0);
args2.CopyTo(array, args.Length);
return array;
}
But this is a bug in the current implementation which has nothing to do with it being an extension method.
Related
I am working with NumSharp and a bit new to it. I would like to set multiple data values and there are two main functions of concern:
NDArray.SetValue(object value, param int[] indices)
NDArray.SetData(object value, param int[] indices)
The first one SetValue expects full index of a single value element, for example, if you have np.zeros(3,3) it expects full indexing, and if there is missing index it will use 0 in its place.
For example:
var arr = np.zeros(3,3);
arr.SetValue(10.0, 0);
arr.SetValue(10.0, 0, 0);
Both lines are equivalent.
On the other hand, SetData will modify an entire sub-array based on the index, and if you give full index, it works like SetValue. For example:
arr.SetData(10.0, 0); // sets all arr[0] values to 10
arr.SetData(10.0, 0, 1); // sets arr[0][1] to 10
Now, for my use case, I would like to redefine a function that accepts multiple values and treats each one as a sub-array index.
arr.SetMultiData(10.0, 0, 1); /// sets arr[0] and arr[10] values to 10
Naturally, I went for an extension method:
public static class Extensions
{
public static void SetMultiData(this NDArray arr, object value, int[] indices)
{
foreach (var i in indices)
arr.SetData(value, i);
}
}
But here is what completely lost me. This code, which does not use extension method, works:
static void Main(string[] args)
{
NDArray arr = np.zeros(3, 3);
int[] indices = new int[] { 0, 1 };
double value = 10;
foreach (var i in indices)
arr.SetData(value, i);
Console.WriteLine(arr.ToString());
}
Output:
[[10, 10, 10],[10, 10, 10],[0, 0, 0]]
This code doesn't:
static void Main(string[] args)
{
NDArray arr = np.zeros(3, 3);
int[] indices = new int[] { 0, 1 };
double value = 10;
arr.SetMultiData(value, indices);
Console.WriteLine(arr.ToString());
}
Output:
[[10, 0, 0],[0, 0, 0],[0, 0, 0]]
This isn't always going to print, in most cases it just fails to print and I believe the underlying arr structure is corrupted accordingly, which leads to undefined behavior when I run it multiple times.
Now, the last bit. When I change the Extension method to accept double instead of object, it works perfectly. But I need the extension method to work with more than one type given it is just a wrapper around SetData. Given this, I also tried changing the method to be generic:
public static class Extensions
{
public static void SetMultiData<T>(this NDArray arr, object value, int[] indices)
{
T val = (T)value;
foreach (var i in indices)
arr.SetData(val, i);
}
}
I was shocked when this didn't work and produced the same output. Basically, I made the explicit cast from object to type. And I called the function like this: arr.SetMultiData<double>(value, indicies); and I used the debugger and compared the code that worked with this and the types are exactly the same ones that are passed to arr.SetValue given i = int and val = double, yet one works and one doesn't.
What causes such behavior? And why does it work when I explicitly set it to double when it fails both using generic/object types?
For reference, here is the SetData implementation. which is open-source.
The reason is double is implicitly convertible to NDArray (by implicit operator defined in NDArray). So when you do:
arr.SetData(value, i);
and value is of type double - the overload being called is SetData(NDArray, int[), because it's the best fit given that double is convertible to NDArray.
However when value is of type object or even of unresolved generic type (T) - then overload being called is SetData(object, int[]), so things go wild (because those overloads do quite different things IF value you pass as object is not NDArray, and it's not - it is double). Overload is chosen at compile time, and there is no way compiler can choose the NDArray overload even in generic case (since overload chosen should fit for any possible generic types you can pass).
In your case you can "solve" this by using NDArray value in your extension method instead of object or generic:
public static class Extensions
{
public static void SetMultiData(this NDArray arr, NDArray value, int[] indices)
{
foreach (var i in indices)
arr.SetData(value, i);
}
}
I am trying to add an extension to Enumerable class. but it seems that the extension FindEven() is not picked by the C# compiler. When I build, the compiler spit error :
CS0117 'Enumerable' does not contain a definition for 'FindEven'
Here is my code:
namespace ConsoleApp1
{
static public class Program
{
static IEnumerable<int> FindEven(this IEnumerable<int> array, Func<int, bool> predicte)
{
foreach (var n in array)
{
if (predicte(n))
{
yield return n;
}
}
}
static public void Main(string[] args)
{
int[] numbers = new[]{ 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = Enumerable.Select(Enumerable.FindEven(numbers, n => n % 2 == 0), n => n);
foreach (var output in result)
Console.WriteLine(output);
Console.ReadLine();
}
}
}
Anything I did incorrectly here?
[edit]
What I am trying to do here is to see how the 'where' statement in the following LINQ works by making my own version of 'Where', which in this case is 'FindEven' (not a good name I have to admit).
var result = from element in numbers
where element % 2 == 0
select element;
I think if I replace 'FindEven' by 'Where' which is defined in Enumerable[from metadata]... It should be the way LINQ works. But I just can not get the code compiled.
Thanks
Per your edit, it seems you're trying to add your FindEven function to the Enumerable class, but that won't work. When you're calling Enumerable.Where, you're not calling an extension method, you're calling an actual static method that's defined in Enumerable. You can't extend that class that way, because extension methods can't extend static methods, that's not what they're for. They extend instance methods.
The equivalent in your code of calling Enumerable.Where is calling Program.FindEven. That's where the static method is defined. The magic of extension methods is having both Where and FindEven available for an instance of IEnumerable<int>, regardless of where they're defined.
Pre-edit
From the way you call the method, you seem to believe that the extension method adds a new static method to the Enumerable class. It doesn't work that way. The extension method you defined will "add" the method to any instance of IEnumerable<int>, so your code will look like this:
var result = numbers.FindEven(n => n % 2 == 0);
Note, though, that your FindEven doesn't actually FindEven - it just queries using the provided predicate, meaning it's exactly the same as the built-in LINQ Where function. A proper FindEven method would be:
static IEnumerable<int> FindEven(this IEnumerable<int> source)
{
return source.Where(n => n % 2 == 0);
}
This will return a lazily-evaluated IEnumerable<int> containing only the even numbers.
Also, your external Select method does nothing - it just maps every integer to itself, meaning it returns an enumerable that's completely equivalent to its input.
When you define an extension method, you have to call it as it was a member function of your this IEnumerable<int> array parameter
simply replace your call with
var result = Enumerable.Select(numbers.FindEven(n => n % 2 == 0), n => n);
Also note that you created an extension method for IEnumerable<int>, not for Enumerable class
As per your edit, you can use Where function.
namespace ConsoleApp1
{
static public class Program
{
static public void Main(string[] args)
{
int[] numbers = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
var result = numbers.Where(n => n % 2 == 0);
foreach (var output in result)
Console.WriteLine(output);
Console.ReadLine();
}
}
}
C# allows creating and populating multidimensional arrays, here is a simple example:
public static void Main(String[] args)
{
var arr = (int[,])CreateArray(new [] {2, 3}, 8);
Console.WriteLine("Value: " + arr[0,0]);
}
// Creates a multidimensional array with the given dimensions, and assigns the
// given x to the first array element
public static Array CreateArray<T>(int[] dimLengths, T x)
{
var arr = Array.CreateInstance(typeof(T), dimLengths);
var indices = new int[dimLengths.Length];
for (var i = 0; i < indices.Length; i++)
indices[i] = 0;
arr.SetValue(x, indices); // Does boxing/unboxing
return arr;
}
This works well. However, for some reason there is no generic version of Array.SetValue(), so the code above does boxing/unboxing, which I'd like to avoid. I was wondering if I missed something or if this is an omission in the .NET API?
No, you are not missing anything: Arrays does not have an option that sets the value without boxing and unboxing.
You do have an alternative to this with LINQ, but it is probably going to be slower than boxing/unboxing for a single element, because compiling a dynamic lambda would "eat up" the potential benefits:
public static Array CreateArray<T>(int[] dimLengths, T x) {
var arr = Array.CreateInstance(typeof(T), dimLengths);
var p = Expression.Parameter(typeof(object), "arr");
var ind = new Expression[dimLengths.Length];
for (var i = 0; i < dimLengths.Length; i++) {
ind[i] = Expression.Constant(0);
}
var v = Expression.Variable(arr.GetType(), "cast");
var block = Expression.Block(
new[] {v}
, new Expression[] {
Expression.Assign(v, Expression.Convert(p, arr.GetType()))
, Expression.Assign(Expression.ArrayAccess(v, ind), Expression.Constant(x))
, Expression.Constant(null, typeof(object))
}
);
Expression.Lambda<Func<object, object>>(block, p).Compile()(arr);
return arr;
}
If you wanted to set all elements in a loop, you could modify the above to compile a dynamically created lambda with multiple nested loops. In this case, you could get an improvement on having to perform multiple boxing and unboxing in a series of nested loops.
for some reason there is no generic version of Array.SetValue()
While it is definitely possible to write a generic method similar to SetValue in the Array class, it may not be desirable. A generic method on a non-generic class would give a false promise of compile-time type safety, which cannot be guaranteed, because the compiler does not know the runtime type of the Array object.
I didn't find any generic ways either to set a value into an Array instance, so I guess the only workaround is to use the unsafe context to avoid boxing.
However, there can be no generic version, now when I think of it. See, when you define a generic method method<T>()..., you do define the parameter for the method: ...<T>(T[] a)... where you have to be specific about the dimensions count, which is one. To create a twodimensional parameter, you define it like this ...<T>(T[,] a)... and so on.
As you can see, by the current syntax of C#, you simple cannot create a generic method, which can accept any-dimensional array.
As you know C# supports variadic methods through the params keyword:
int Add(params int[] xs) {
return xs.Sum();
}
Which can then be called with any number of arguments you like:
Add(1);
Add(1, 2);
Add(1, 2, 3);
But say I want to call Add using an array of ints1. Is this possible and how (preferably without reflection)? I tried the following but they gave syntax errors (the syntax was pure guessing):
var xs = new[] { 1, 2, 3 };
Add(xs...); // doesn't work; syntax error
Add(params xs); // doesn't work; syntax error
1 My actual use-case is different but I thought this example would be less complicated.
Your method needs a return type:
int Add(params int[] xs) {
return xs.Sum();
}
And to call it with an array you just use the ordinary syntax for method calls:
int[] xs = new[] { 1, 2, 3 };
var result = Add(xs);
The params keyword basically just allows you to take advantage of a little syntactic sugar. It tells the compiler that when it sees
Add(1, 2, 3);
It should convert that to
Add(new int[] { 1, 2, 3});
So to do this from your code, you don't have to do anything special.
int[] parameters = new int[] { ... }
results = Add(parameters);
See the documentation for more details.
As far as I know, you can just call it with an array like you would a normal method:
Add(xs);
Nothing fancy, no params keyword on the method call, no dots.
static void Main(string[] args)
{
int[] tmp = {1, 2};
var sum = Add(tmp);
}
public static int Add(params int[] xs)
{
return xs.Sum();
}
Should work just fine..
If it's anything like Java, you can just call the method with the array as an argument.
This feature is also what makes varargs dangerous, especially if one of the vararg types is also an array...
A question to all of you C# wizards. I have a method, call it myFunc, and it takes variable length/type argument lists. The argument signature of myFunc itself is myFunc(params object[] args) and I use reflection on the lists (think of this a bit like printf, for example).
I want to treat myFunc(1, 2, 3) differently from myFunc(new int[] { 1, 2, 3 }). That is, within the body of myFunc, I would like to enumerate the types of my arguments, and would like to end up with { int, int, int} rather than int[]. Right now I get the latter: in effect, I can't distinguish the two cases, and they both come in as int[].
I had wished the former would show up as obs[].Length=3, with obs[0]=1, etc.
And I had expected the latter to show up as obs[].Length=1, with obs[0]={ int[3] }
Can this be done, or am I asking the impossible?
Well this will do it:
using System;
class Program
{
static void Main(string[] args)
{
Console.WriteLine("First call");
Foo(1, 2, 3);
Console.WriteLine("Second call");
Foo(new int[] { 1, 2, 3 });
}
static void Foo(params object[] values)
{
foreach (object x in values)
{
Console.WriteLine(x.GetType().Name);
}
}
}
Alternatively, if you use DynamicObject you can use dynamic typing to achieve a similar result:
using System;
using System.Dynamic;
class Program
{
static void Main(string[] args)
{
dynamic d = new ArgumentDumper();
Console.WriteLine("First call");
d.Foo(1, 2, 3);
Console.WriteLine("Second call");
d.Bar(new int[] { 1, 2, 3 });
}
}
class ArgumentDumper : DynamicObject
{
public override bool TryInvokeMember
(InvokeMemberBinder binder,
Object[] args,
out Object result)
{
result = null;
foreach (object x in args)
{
Console.WriteLine(x.GetType().Name);
}
return true;
}
}
Output of both programs:
First call
Int32
Int32
Int32
Second call
Int32[]
Now given the output above, it's not clear where your question has really come from... although if you'd given Foo("1", "2", "3") vs Foo(new string[] { "1", "2", "3" }) then that would be a different matter - because string[] is compatible with object[], but int[] isn't. If that's the real situation which has been giving you problems, then look at the dynamic version - which will work in both cases.
OK, so let's say that we abandon the other question where you incorrectly believe that any of this is a compiler bug and actually address your real question.
First off, let's try to state the real question. Here's my shot at it:
The preamble:
A "variadic" method is a method which takes an unspecified-ahead-of-time number of parameters.
The standard way to implement variadic methods in C# is:
void M(T1 t1, T2 t2, params P[] p)
that is, zero or more required parameters followed by an array marked as "params".
When calling such a method, the method is either applicable in its normal form (without params) or its expanded form (with params). That is, a call to
void M(params object[] x){}
of the form
M(1, 2, 3)
is generated as
M(new object[] { 1, 2, 3 });
because it is applicable only in its expanded form. But a call
M(new object[] { 4, 5, 6 });
is generated as
M(new object[] { 4, 5, 6 });
and not
M(new object[] { new object[] { 4, 5, 6 } });
because it is applicable in its normal form.
C# supports unsafe array covariance on arrays of reference type elements. That is, a string[] may be implicitly converted to object[] even though attempting to change the first element of such an array to a non-string will produce a runtime error.
The question:
I wish to make a call of the form:
M(new string[] { "hello" });
and have this act like the method was applicable only in expanded form:
M(new object[] { new string[] { "hello" }});
and not the normal form:
M((object[])(new string[] { "hello" }));
Is there a way in C# to implement variadic methods that does not fall victim to the combination of unsafe array covariance and methods being applicable preferentially in their normal form?
The Answer
Yes, there is a way, but you're not going to like it. You are better off making the method non-variadic if you intend to be passing single arrays to it.
The Microsoft implementation of C# supports an undocumented extension that allows for C-style variadic methods that do not use params arrays. This mechanism is not intended for general use and is included only for the CLR team and others authoring interop libraries so that they can write interop code that bridges between C# and languages that expect C-style variadic methods. I strongly recommend against attempting to do so yourself.
The mechanism for doing so involves using the undocumented __arglist keyword. A basic sketch is:
public static void M(__arglist)
{
var argumentIterator = new ArgIterator(__arglist);
object argument = TypedReference.ToObject(argumentIterator.GetNextArg());
You can use the methods of the argument iterator to walk over the argument structure and obtain all the arguments. And you can use the super-magical typed reference object to obtain the types of the arguments. It is even possible using this technique to pass references to variables as arguments, but again I do not recommend doing so.
What is particularly awful about this technique is that the caller is required to then say:
M(__arglist(new string[] { "hello" }));
which frankly looks pretty gross in C#. Now you see why you are better off simply abandoning variadic methods entirely; just make the caller pass an array and be done with it.
Again, my advice is (1) under no circumstances should you attempt to use these undocumented extensions to the C# language that are intended as conveniences for the CLR implementation team and interop library authors, and (2) you should simply abandon variadic methods; they do not appear to be a good match for your problem space. Do not fight against the tool; choose a different tool.
Yes, you can, checking the params length and checking the argument type, see the following working code sample:
class Program
{
static void Main(string[] args)
{
myFunc(1, 2, 3);
myFunc(new int[] { 1, 2, 3 });
}
static void myFunc(params object[] args)
{
if (args.Length == 1 && (args[0] is int[]))
{
// called using myFunc(new int[] { 1, 2, 3 });
}
else
{
//called using myFunc(1, 2, 3), or other
}
}
}
You can achieve something like this by breaking the first element out of the list and providing an extra overload, for example:
class Foo
{
public int Sum()
{
// the trivial case
return 0;
}
public int Sum(int i)
{
// the singleton case
return i;
}
public int Sum(int i, params int[] others)
{
// e.g. Sum(1, 2, 3, 4)
return i + Sum(others);
}
public int Sum(int[] items)
{
// e.g. Sum(new int[] { 1, 2, 3, 4 });
int i = 0;
foreach(int q in items)
i += q;
return i;
}
}
This is not possible in C#. C# will replace your first call by your second call at compile time.
One possibility is to create an overload without params and make it a ref parameter. This probably won't make sense. If you want to change behavior based on the input, perhaps give the second myFunc another name.
Update
I understand your issue now. What you want is not possible. If the only argument is anything that can resolve to object[] it's impossible to distinguish from this.
You need an alternative solution, maybe have a dictionary or array created by the caller to build up the parameters.
Turns out that there was a real issue, and it comes down to the way that C# does type inference. See the discussion on this other thread