Passing around member functions in C# - c#

Mostly it comes handy that C# delegates already store the object together with the member function. But is there a way, to store -- and pass as parameters -- only the member function itself, just as the good old pointer-to-member-function in C++?
In case the description is less than clear, I give a self-contained example. And, yes, in the example the insistence to pass around member functions is totally pointless, but I have more serious uses for this.
class Foo {
public int i { get; set; }
/* Can this be done?
public static int Apply (Foo obj, ???? method, int j) {
return obj.method (j);
}
*/
public static int ApplyHack (Foo obj, Func<int, int> method, int j) {
return (int) method.Method.Invoke (obj, new object [] { j });
}
public static readonly Foo _ = new Foo (); // dummy object for ApplyHack
public int Multiply (int j) {
return i * j;
}
public int Add (int j) {
return i + j;
}
}
class Program {
static void Main (string [] args) {
var foo = new Foo { i = 7 };
Console.Write ("{0}\n", Foo.ApplyHack (foo, Foo._.Multiply, 5));
Console.Write ("{0}\n", Foo.ApplyHack (foo, Foo._.Add, 5));
Console.ReadKey ();
}
}
You see, the only workaround I've found is rather ugly and probably slow.

What you want is something called an open instance delegate. I've written about them on my blog
Basically, you can create a delegate to an instance method without tying it to a particular instance, and specify the instance to use it on when you call it:
class Foo {
public int i { get; set; }
public int Multiply (int j) {
return i * j;
}
public int Add (int j) {
return i + j;
}
}
class Program {
static void Main (string [] args) {
Func<Foo, int, int> multiply = (Func<Foo, int, int>)Delegate.CreateDelegate(typeof(Func<Foo, int, int>), null, typeof(Foo).GetMethod("Multiply");
Func<Foo, int, int> add = (Func<Foo, int, int>)Delegate.CreateDelegate(typeof(Func<Foo, int, int>), null, typeof(Foo).GetMethod("Add");
var foo1 = new Foo { i = 7 };
var foo2 = new Foo { i = 8 };
Console.Write ("{0}\n", multiply(foo1, 5));
Console.Write ("{0}\n", add(foo1, 5));
Console.Write ("{0}\n", multiply(foo2, 5));
Console.Write ("{0}\n", add(foo2, 5));
Console.ReadKey ();
}
}

Taking your existing code:
public static int ApplyHack (Foo obj, Func<int, int> method, int j) {
return (int) method.Method.Invoke (obj, new object [] { j });
}
You could do something like this:
public static int ApplyHack (Foo obj, Func<int, int> method, int j) {
var func = (Func<int,int>)Delegate.CreateDelegate(typeof(Func<int,int>), obj, method.Method);
return func(j);
}
This will create a new delegate around the method and the new object. To take your first example:
public static int Apply (Foo obj, ???? method, int j) {
return obj.method (j);
}
The type you are looking for is System.Reflection.MethodInfo and it would look like this:
public static int Apply (Foo obj, MethodInfo method, int j) {
var func = (Func<int,int>)Delegate.CreateDelegate(typeof(Func<int,int>), obj, method);
return func(i);
}
Note that while you are allocating delegates for each invocation, I believe this will still be faster than using reflection, since you do not have to box function input/output, nor store it in object[] arrays.

Assuming you're using C# 2.0 or above, and have access to anonymous delegates, you can do it very simply by wrapping the function in an anonymous delegate at the point of storage:
class Foo
{
public Foo(int v)
{
this.v = v;
}
int v;
public int Multiply(int x)
{
return v * x;
}
public int Add(int x)
{
return v+x;
}
delegate int NewFunctionPointer(Foo, int);
delegate int OldDelegateStyle(int);
static void Example()
{
Foo f = new Foo(2);
Foo f2 = new Foo(3);
// instead of this, which binds an instance
OldDelegateStyle oldMul = f.Multiply;
// You have to use this
NewFunctionPointer mul = delegate(Foo f, int x) { return f.Multiply(x); }
NewFunctionPointer add = delegate(Foo f, int x) { return f.Add(x); }
// But can now do this
mul(f, 4); // = 8
add(f2, 1); // = 3
}
}

If you're okay with passing the this reference as a parameter, why not just use static methods?
class Foo {
public int i;
public static int ApplyHack(Foo foo, Func<Foo, int, int> method, int j) {
return method(foo, j);
}
public static int Multiply(Foo foo, int j) {
return foo.i * j;
}
}
Console.Write("{0}\n", Foo.ApplyHack(foo, Foo.Multiply, 5));
This mainly affects how you construct the Foo object, without changing how you use it. It also doesn't prevent you from having a non-static int Multiply(int) method.

You could retrieve and reuse the MethodInfo for the method or just use the name and extract the method at runtime.
public static int ApplyHack (Foo obj, string methodName, int j)
{
var method = typeof(Foo).GetMethod(methodName);
return (int) method.Invoke (obj, new object [] { j });
}
I'd be very careful that this was actually necessary as it seems like a code smell to me.

You can do it that way
class Foo
{
public int i { get; set; }
public static int Apply(Foo obj, Func<int, int, int> method, int j)
{
return method(j, obj.i);
}
public static int Multiply(int j, int i)
{
return i * j;
}
public static int Add(int j, int i)
{
return i + j;
}
}
static void Main(string[] args)
{
var foo = new Foo { i = 7 };
Console.Write("{0}\n", Foo.Apply(foo, Foo.Multiply, 5));
Console.Write("{0}\n", Foo.Apply(foo, Foo.Add, 5));
Console.ReadKey();
}

I think you can do this easily with this if I understand correctly:
public static int Apply(Func<int, int> method, int j)
{
return (int)method.Method.Invoke(method.Target, new object[] { j });
}
and call it like this:
Console.Write("{0}\n", Foo.Apply(foo.Multiply, 5));

Related

How do I make a class usable as a 2d array in c#?

Code
Class
using System;
using System.Collections.Generic;
// In case you really, really want to know,
// this class is called Ticket in my real code and it's used as a Tambola ticket.
public class Foo
{
public int?[,] Value { get; private set; } = new int?[3 , 9]
public Foo () => Value = InternalGenerate()
public static Foo Generate () => new() { Value = InternalGenerate() };
protected static int?[,] InternalGenerate ()
{
// Generate a random instance of this class.
}
}
Usage
class Program
{
static void Main ()
{
Bar( new() );
}
static void Bar ( Foo foo )
{
int width = foo.GetLength(1);
int height = foo.GetLength(0);
int?[] array = foo.Cast<int?>().ToArray(); // convert foo to 1d array.
// get "corners" of foo
int tl = (int) array.First(e => e.HasValue);
int fl = (int) array.Take(width).Last(e => e.HasValue);
int lf = (int) array.Skip(( height - 1 ) * width).First(e => e.HasValue);
int ll = (int) array.Last(e => e.HasValue);
// this code comes from
// stackoverflow.com/questions/68661235/get-corners-of-a-2d-array
}
}
This code will fail.
Question
I have a class called Foo.
It's actual value should be a 2d array of type int? - however, when I try to use an instance of Foo as a int?[,] (look at Usage section of code), it fails (which is expected).
So, how do I make it possible to use an instance of Foo as if it is a collection, instead of having to use {instance of Foo}.Value?
I'm not sure, but I believe this is called a wrapper.
What I've tried
I tried to inherit Foo from IEnumerable - but I've never created a custom collection type before. So, after hours of reading tutorials and staring at C#'s source code, I finally resorted to asking a question here, on stack overflow.
BUT, as I said, I wasn't able to implement IEnumerable - doing that may still be the answer to my question. In that case, please tell me how to do so.
This is probably my longest question yet
Actually there are still a few errors or problems in your code:
some syntax errors (missing semi-colons)
rowCount and columnCount do not necessarily reflect the actual values for your 'Valye' array.
public IEnumerator<int?> GetEnumerator () => (IEnumerator<int?>) Value.GetEnumerator(); will throw an 'InvalidCast' runtime exception.
In the code below those issues are solved:
public class Foo: IEnumerable<int?>
{
public int?[,] Value { get; private set; } = new int?[3, 9];
public int? this[int x, int y]
{
get => Value[x, y];
set => Value[x, y] = value;
}
public Foo() => Value = InternalGenerate();
public static Foo Generate() => new() { Value = InternalGenerate() };
protected static int?[,] InternalGenerate()
{
// Generate a random instance of this class.
return new int?[3, 9]
{
{1,2,3,4,5,6,7,8,9 },
{9,8,null,6,5,4,3,2,1 },
{1,2,3,4,5,6,7,8,9 }
};
}
public int GetLength(int dimension) => Value.GetLength(dimension);
public IEnumerator<int?> GetEnumerator()
{
foreach (var item in Value)
{
yield return item;
}
}
IEnumerator IEnumerable.GetEnumerator()
{
return Value.GetEnumerator();
}
}
Then you can instantiate a variable of type Foo (not an int?[,]) and use it as you want:
static void Bar(Foo foo)
{
int width = foo.GetLength(1);
int height = foo.GetLength(0);
var array = foo; // convert foo to 1d array.
// get "corners" of foo
int tl = (int)array.First(e => e.HasValue);
int fl = (int)array.Take(width).Last(e => e.HasValue);
int lf = (int)array.Skip((height - 1) * width).First(e => e.HasValue);
int ll = (int)array.Last(e => e.HasValue);
// this code comes from
// stackoverflow.com/questions/68661235/get-corners-of-a-2d-array
}
If you want to be able to cast Foo to an int?[,], you will need to implement the cast-operations as well (if that is really necessary).
Here's what I eventually landed with:
Class
using System;
using System.Collections;
using System.Collections.Generic;
// In case you really, really want to know,
// this class is called Ticket in my real code and it's used as a Tambola ticket.
public class Foo
{
protected int?[,] Value { get; set; } = new int?[3 , 9];
public Foo () => Value = InternalGenerate();
public static Foo Generate () => new() { Value = InternalGenerate() };
protected static int?[,] InternalGenerate ()
{
// Generate a random instance of this class.
}
public IEnumerator<int?> GetEnumerator () => (IEnumerator<int?>) Value.GetEnumerator();
IEnumerator IEnumerable.GetEnumerator () => Value.GetEnumerator();
public int? this[int r , int c]
{
get => Value [r , c];
private set => Value [r , c] = value;
}
public const rowCount = 3;
public const columnCount = 9;
}
Usage
class Program
{
static void Main ()
{
Bar( new() );
}
static void Bar ( Foo foo )
{
int?[] array = foo.Cast<int?>().ToArray(); // convert foo to 1d array.
//get "corners" of foo
int topLeft = (int) array.First(e => e.HasValue);
int topRight(int) array.Take(columnCount).Last(e => e.HasValue);
int bottomLeft = (int) array.Skip(( rowCount - 1 ) * columnCount).First(e => e.HasValue);
int bottomRight = (int) array.Last(e => e.HasValue);
}
}

Linq: How to assign local variables within linq from array

I have a method that returns a simple int[2]. Both elements of the array need to each be assigned to an int r and int c local variables. I want to achieve this all within a single Linq query. Any way to do this?
This is pseudo code of what I want to achieve, but obviously it doesn't work. I don't know Linq very well and I'm trying to get better at it. Method(r,c) is the method that returns an int[2]. I want to pull each element out and assign int[0] = r and int[1] = c.
void Foo(int r, int c)
{
Method(r,c).Select(([0],[1]) => { r = [0]; c = [1]; });
}
int[] Method(int r, int c)
{
///stuff///
}
logic:
Method return int array with r and c
create static class to make a Select method
Select method input the int array and call Func<int[],T> and retrun T (T is Generic)
Online Test Demo Link
public class Program
{
public static void Main()
{
var result = Method(1, 2).Select( (r,c) => new { r,c });
Console.WriteLine(result);
}
static int[] Method(int r,int c) => new[] {r,c};
}
public static class LinqExtension
{
public static T Select<T>(this int[] ints, Func<int,int, T> func) => func(ints[0],ints[1]);
}
or you can use Method with params int[]
public class Program
{
public static void Main()
{
var value = Method(1, 2).Select((int[] arr) => new{r = arr[0],c = arr[1]});
Console.WriteLine(value); //result : { r = 1, c = 2 }
}
public static int[] Method(params int[] ints)
{
return ints;
}
}
public static class LinqExtension
{
public static T Select<T>(this int[] ints,Func<int[],T> func){
return func(ints);
}
}
new question :
How would I use in out keywords in this context? Assuming I'm using parameters passed by value from Foo().
you can use out keyword :
public class Program
{
public static void Main()
{
Method(1, 2).Select( out int r ,out int c);
Console.WriteLine(r);
Console.WriteLine(c);
}
static int[] Method(int r,int c) => new[] {r,c};
}
public static class LinqExtension
{
public static void Select(this int[] ints, out int r, out int c)
{
r = ints[0];
c = ints[1];
}
}

Is it possible to construct anonymous/delegates like the following in C#?

Is there anyway to accomplish the following? Here is some simplified semi pseudo code of what I am trying to do:
class Foo {
static public FUNCTION one(int foo, int bar) {
return List<Vector> FUNCTION(int num) {
List<Vector> v = new List<Vector>();
for (int i = 0; i < num; i++) {
v.Add( new Vector(1+foo, 1+bar) );
}
return v;
}
static public FUNCTION two(int foo, int bar) {
return List<Vector> FUNCTION(int num) {
List<Vector> v = new List<Vector>();
// Do something else?
return v;
}
}
}
Then I would like to call it like so:
generic = Foo.one(1, 2);
List<Vector> v = generic(2);
generic = Foo.two(1, 2);
List<Vector> v = generic(2);
I think this is kind of along the lines of what I want but I'm unsure how to pass the first set of arguments in.
public static Func<int, int, List<Vector>> one()
{
Func<int, List<Vector>> func = (int num) =>
{
List<Vector> v = new List<Vector>();
return v;
};
return func;
}
Is that a solution for your problem? It is a construct called Closure. It is just a combination of what you already had.
public static Func<int, List<Vector>> one(int foo, int bar)
{
Func<int, List<Vector>> func =
num =>
{
List<Vector> v = new List<Vector>();
for (int i = 0; i < num; i++)
{
v.Add(new Vector(1 + foo, 1 + bar));
}
return v;
};
return func;
}

C# Function Overloading : Ambiguous Call

Getting the ambiguous call as arrangement of parameters are different: short,int / int,short /byte,int / int,byte
As Function signature is:
1.Number of arguments/parameters
2.Type of arguments/parameters
3.Arrangement of arguments/parameters
Why the call is ambiguous ? should it belongs to the similar type ?...
code:
class Program
{
static void Main(string[] args)
{
test abbb = new test();
//abbb.add(2.2f,1);
// abbb.add(2,2.2f);
abbb.add(255,1);
abbb.add(1,256);
Console.ReadLine();
}
}
class test
{
public int add(byte i , int f) {
return i + f;
}
public int add(int i, byte f)
{
return i + f;
}
public int add(short i, int f)
{
return i + f;
}
public int add(int i, short f)
{
return i + f;
}
}
By default, any 'Magic Number' will be treated as an Integer, but sometimes for ease of use the compiler can convert to another number format implicitly if it has enough information to do so.
In order to get around the ambiguous calls, you would be best explicitly defining typed variables for the numbers first, then passing them into the functions to remove any ambiguity.
class Program
{
static void Main(string[] args)
{
test abbb = new test();
Console.WriteLine(abbb.add(32767, 32770)); //short , int
Console.WriteLine(abbb.add(32770, 32767)); //int ,short
Console.WriteLine(abbb.add(255, 32770)); // byte,int
Console.WriteLine(abbb.add(32770, 255)); // int,byte
Console.ReadLine();
}
}
class test
{
public int add(byte f, int i)
{
return i + f;
}
public int add(int i, byte f)
{
return i + f;
}
public int add(short i, int f)
{
return i + f;
}
public int add(int f, short i)
{
return i + f;
}
}
No ambiguity due to mentioned specific range of types...
It is ambiguous because C# does not know which of the two numbers is which of the types.
Both 1 and 256 may a be a short and both may be an int.
You may use explicit casting to "choose" one of the methods.

How can i make params `out` in C#?

I find myself in the situation requiring this
public static void Fill(this SomeClass c, params out object[] p)
and calling it as
c.Fill(out i, out i2, out sz, out i3, out sz2);
However i get the error error CS1611: The params parameter cannot be declared as ref or out
How can i pass in variable length arguments and make them writeable? All of these are a mixture of ints and strings
You can't have it treat the arguments as out (or ref) and make use of the params feature at the same time. It simply doesn't work. The best you can do is to create an array parameter, make the array out, declare an array variable and call the method passing the array, then inspect each element manually by index.
Foo(out object[] data) {...}
object[] result;
Foo(out result);
// look at result[0], result[1], result[2] etc
So: you cannot do what you want. Even if you could, ref / out never work unless there is an exact match between data type, so it would still have to be:
object o1, o2, o3, o4;
Foo(out o1, out o2, out o3, out o4);
// cast o1, o2, o3, o4
Which still isn't what you want.
There is no technical need for out here. This works:
void Fill(object[] p)
{
p[0] = 1;
p[1] = 42;
p[2] = "Hello";
p[3] = -1;
p[4] = "World";
}
object[] p = new object[5];
foo.Fill(p);
i = (int)p[0];
i2 = (int)p[1];
sz = (string)p[2];
i3 = (int)p[3];
sz2 = (string)p[4];
You could return your values as Tuple:
(define your own tuple class if you're not using .NET4.0)
static Tuple<int, string> Fill()
{
return new Tuple(42, "Hello World");
}
and then define extension methods to unpack tuples:
public static class TupleExtensions
{
public static void Unpack<T1, T2>(
this Tuple<T1, T2> tuple,
out T1 item1,
out T2 item2)
{
item1 = tuple.Item1;
item2 = tuple.Item2;
}
}
Then you can write this:
int i;
string sz;
foo.Fill().Unpack(out i, out sz);
1) If you can avoid the need to get the values in declared variables, then passing the array and populating it is the best option, as shown by dtb's answer.
2) Otherwise you can have a simple wrapper for your variable.
public class Wrapper //or may be generic?
{
public object Value { get; set; }
public Wrapper(object value)
{
Value = value;
}
}
Now you can call
var i = new Wrapper(0), i2 = new Wrapper(0), i3 = new Wrapper(0);
c.Fill(i, i2, i3);
i.Value //your value here
public static void Fill(this SomeClass c, params Wrapper[] p)
{
for (int i = 0; i < p.Length; i++)
{
p[i].Value = 1; //assigning
}
}
You will have to deal with Value property after calling Fill method.
3) You can make use of closure. Something like the Ref<T> class implemented as shown:
public static class Ref
{
public static Ref<T>[] Create<T>(params Expression<Func<T>>[] getters)
{
return getters.Select(Create).ToArray();
}
public static Ref<T> Create<T>(Expression<Func<T>> getter)
{
return new Ref<T>(getter);
}
}
public sealed class Ref<T>
{
readonly Func<T> getter;
readonly Action<T> setter;
public Ref(Expression<Func<T>> getter)
{
var output = getter.Body;
var input = Expression.Parameter(output.Type); //or hardcode typeof(T)
var assign = Expression.Assign(output, input);
var setter = Expression.Lambda<Action<T>>(assign, input);
this.getter = getter.Compile();
this.setter = setter.Compile();
}
public T Value
{
get { return getter(); }
set { setter(value); }
}
}
public static void Fill(this SomeClass c, params Ref<object>[] p)
//assign inside
object i = 0, i2 = 0, i3 = 0;
c.Fill(Ref.Create(() => i, () => i2, () => i3));
//i, i2 etc changed
Few things to note:
All the above approaches are basically ref approaches, compiler doesn't simply force assigning value of parameters inside the method before the control leaves as in the case of out which is your question, but as far as I know out is not possible here.
I like the first one, simple, and conventional. If not possible my vote is for 3rd approach.
As others have talked about, you can only pass the exact same type as ref/out parameters. So if your method by definition takes arbitrary references of object type, you have to declare even your variables as object locally. In the last approach, you can make the whole thing generic like by changing parameter type to Ref<T> from Ref<object> but that means your all local variables should also be one T.
You can use a dictionary structure to cache Ref<T> to avoid recompiling same trees.
The same implementation can be used to pass properties and variables as method arguments or return values by reference.
As others have said, you can't use params and out together. You have to construct an array at the call site.
This is because params tells the compiler to do the same thing - construct an array from the specified arguments. Unfortunately, when the compiler creates the array, you don't get a reference to it; even if the variable is written with a new array, you can never get to it.
I would guess you are asking for a thin metal ruler. What problem are you trying to solve with this mechanism?
I think I might have an answer to your question; Consider the following code snippet, with the main "InvokeMemberMethod" function doing the job you ask for. I encountered the same problem as you and came up with this solution:
Note: the "isOutXX" parameter specifies if the preceeding parameter is an "out" parameter.
static object InvokeMemberMethod(object currentObject, string methodName, int argCount,
ref object arg1, bool isOut1,
ref object arg2, bool isOut2,
ref object arg3, bool isOut3,
ref object arg4, bool isOut4,
ref object arg5, bool isOut5,
ref object arg6, bool isOut6)
{
if (string.IsNullOrEmpty(methodName))
{
throw new ArgumentNullException("methodName");
}
if (currentObject == null)
{
throw new ArgumentNullException("currentObject");
}
Type[] argTypes = null;
object[] args = null;
if (argCount > 0)
{
argTypes = new Type[argCount];
args = new object[argCount];
argTypes[0] = arg1.GetType();
if (isOut1)
{
argTypes[0] = arg1.GetType().MakeByRefType();
}
args[0] = arg1;
if (argCount == 2)
{
argTypes[1] = arg2.GetType();
if (isOut2)
{
argTypes[1] = arg2.GetType().MakeByRefType();
}
args[1] = arg2;
}
if (argCount == 3)
{
argTypes[2] = arg3.GetType();
if (isOut3)
{
argTypes[2] = arg3.GetType().MakeByRefType();
}
args[2] = arg3;
}
if (argCount == 4)
{
argTypes[3] = arg4.GetType();
if (isOut4)
{
argTypes[3] = arg4.GetType().MakeByRefType();
}
args[3] = arg4;
}
if (argCount == 5)
{
argTypes[4] = arg5.GetType();
if (isOut5)
{
argTypes[4] = arg5.GetType().MakeByRefType();
}
args[4] = arg5;
}
if (argCount == 6)
{
argTypes[5] = arg6.GetType();
if (isOut6)
{
argTypes[5] = arg6.GetType().MakeByRefType();
}
args[5] = arg6;
}
}
MethodInfo methodInfo = currentObject.GetType().GetMethod(methodName, argTypes);
int retryCount = 0;
object ret = null;
bool success = false;
do
{
try
{
//if (methodInfo is MethodInfo)
{
Type targetType = currentObject.GetType();
ParameterInfo[] info = methodInfo.GetParameters();
ParameterModifier[] modifier = new ParameterModifier[] { new ParameterModifier(info.Length) };
int i = 0;
foreach (ParameterInfo paramInfo in info)
{
if (paramInfo.IsOut)
{
modifier[0][i] = true;
}
i++;
}
ret = targetType.InvokeMember(methodName, BindingFlags.InvokeMethod, null, currentObject, args,
modifier, null, null);
//ret = ((MethodInfo)methodInfo).Invoke(currentObject, args,);
success = true;
}
//else
{
// log error
}
}
catch (TimeoutException ex)
{
}
catch (TargetInvocationException ex)
{
throw;
}
retryCount++;
} while (!success && retryCount <= 1);
if (argCount > 0)
{
if (isOut1)
{
arg1 = args[0];
}
if (argCount == 2)
{
if (isOut2)
{
arg2 = args[1];
}
}
if (argCount == 3)
{
if (isOut3)
{
arg3 = args[2];
}
}
if (argCount == 4)
{
if (isOut4)
{
arg4 = args[3];
}
}
if (argCount == 5)
{
if (isOut5)
{
arg5 = args[4];
}
}
if (argCount == 6)
{
if (isOut6)
{
arg6 = args[5];
}
}
}
return ret;
}
public int OutTest(int x, int y)
{
return x + y;
}
public int OutTest(int x, out int y)
{
y = x + 1;
return x+2;
}
static void Main(string[] args)
{
object x = 1, y = 0, z = 0;
Program p = new Program();
InvokeMemberMethod(p, "OutTest", 2,
ref x, false,
ref y, true,
ref z, false,
ref z, false,
ref z, false,
ref z, false);
}
You could pass an array by ref.
Edit:
This would of course change your calling method:
object[] array = new object[] { i, i2, sz, i3, sz2 };
c.Fill(ref array);
I don't imagine the original questioner needs this answer 11 years later, but I found this old question when searching for an overlapping requirement... and thinking through the answer to this question made me realise why my related one wouldn't work very neatly.
For this question, it was implied that the caller needs to communicate the number of parameters and their types to the Fill(...) function - how else does it match up the types to the calling site?
The intended syntax at the calling site could be achieved like this:
public class SomeClass { }
public static void Fill(this SomeClass c, Type[] outputTypes, out object[] p)
{
p = new object[outputTypes.Length];
// TODO: implementation to fill array with values of the corresponding types.
}
// this overload can be removed if "fill" of an empty array is meaningless.
public static void Fill(this SomeClass c)
{
c.Fill(new Type[0], out _);
}
public static void Fill<T>(this SomeClass c, out T r1)
{
c.Fill(new[] { typeof(T) }, out var p);
r1 = (T)p[0];
}
public static void Fill<T1, T2>(this SomeClass c, out T1 r1, out T2 r2)
{
c.Fill(new[] { typeof(T1), typeof(T2) }, out var p);
r1 = (T1)p[0];
r2 = (T2)p[1];
}
// ... extend as required depending on maximum number of out parameters that might be needed
// in particular the 5-parameter version is included in this sample to make OP's sample code line work.
public static void Fill<T1, T2, T3, T4, T5>(this SomeClass c, out T1 r1, out T2 r2, out T3 r3, out T4 r4, out T5 r5)
{
c.Fill(new[] { typeof(T1), typeof(T2), typeof(T3), typeof(T4), typeof(T5) }, out var p);
r1 = (T1)p[0];
r2 = (T2)p[1];
r3 = (T3)p[2];
r4 = (T4)p[3];
r5 = (T5)p[4];
}
public static void someFunction()
{
SomeClass c = new SomeClass();
int i, i2, i3;
string sz, sz2;
// the line below is exactly as shown in question.
c.Fill(out i, out i2, out sz, out i3, out sz2);
}

Categories

Resources