C# - take method as an argument without specifying args - c#

I have a method that needs to take the function and its owner class as the input arguments and then to proceed with its names with reflection. Of course I could take a couple of strings as the input args, so the method would look like this:
void Proceed(string className, string methodName)
{
Console.WriteLine($"{className}.{methodName} called");
//...
}
and would be called like this:
Proceed(nameof(Foo), nameof(Foo.Bar));
But I wonder if there is any way to avoid writing the nameof keyword every time I am trying to call the method. To me something like Proceed<Foo>(f => f.Bar) would look a lot nicer.
I suppose this could be solved with expressions. The problem I face is that if method Bar has arguments, you have to specify them explicitly when calling the method (which seems excessive in my case where you only going to need the name of the method later).
So, the best solution I managed to find is this one:
void Proceed<T>(Expression<Func<T, Action<object, object>>> expression)
{
//...
}
Yet it still specifies the argument method's signature in its own args and therefore is not generic enough.
I wonder if there is some way to pass the function as an argument without specifying its arguments (provided I am only going to need its name later, just like nameof keyword does).

I think you are looking for the usage of an event or delegate.
You can look up more information regarding those 2 online. The idea is that they store some methods with the same arguments so that later it knows what kind of arguments you need to call.

Yes, use the Delegate keyword.
See the answer here.

Related

Passing a Delegate[] to a method

I am trying to desconstruct a method written by another coder to see how it works but it's getting a bit confusing
We have a Delegate that takes an Action as parameter.
private delegate void FunctionDelegate(Action next);
A function is then called in the constructor that takes an array of these delegates as parameter and executes each value in the array
LoadInSeries(LoadRoleAreaHours, LoadTableData);
The Function looks like this
private void LoadInSeries(params FunctionDelegate[] _delegates)
{
var Delegates = new List<FunctionDelegate>(_delegates);
Func<Action, Action> creator = null;
creator = delegate(Action inner)
{
if (Delegates.Count > 0)
{
FunctionDelegate L = Delegates.First();
Delegates.RemoveAt(0);
Action next = delegate { L(inner); };
return creator(next);
}
else return inner;
};
Action outer = creator(null);
if (outer != null)
outer();
}
The intent was too chain a series of functions with each function calling the next but wouldnt it just be easy to use a multicast delegate and add each function to the invocation list.
Does this code do something different?
Without a good Minimal, Complete, and Verifiable code example, it's impossible to understand the code fully. Of most significant concern is that your code example includes a mysterious L() method, the behavior of which we have no idea. There is also the problem with the FunctionDelegate = Delegates.First(); statement. At best, the FunctionDelegate identifier refers to a class field or property; at worst, that statement won't even compile. Either way, there's no evidence that the delegate object being removed from the input list is ever actually invoked.
So to even answer the question, some basic assumptions have to be made, which may or may not be correct.
That said, in the best-case scenario — which is that the code has hidden some awful, convoluted mechanism in the L() method that ultimately winds up able to invoke the delegate for the current call to the creator delegate — the code you're looking at is not simply invoking delegates in sequence, as would be the case with a MulticastDelegate. Rather, the code is constructing a chain of calls to the L() method, passing each delegate instance to the method in sequence.
Since you didn't show us the L() method, there's no way to say what the code actually does. I would agree that if all that L() does is invoke the delegate you pass to it, then this code looks like a very complicated way to just invoke an array of delegates. But, giving the benefit of the doubt to the person who wrote the code, this simply means that L() probably does something other than simply invoke the delegate.
Of course, it's possible the author of the code doesn't deserve the benefit of the doubt. In that case, not only would it be simpler to just use a multicast delegate, the simplest implementation would just iterate over the array, invoking each delegate in the desired sequence. But I say that without really knowing what the code does. I'm just assuming it's intended to do something useful with the delegates that are passed to it. There's no evidence in the code you posted to support even that generous assumption.
Give us the full picture, and a more definitive answer can be provided. Without knowing what L() is, or what side effects might exist in the passed-in delegates' target methods, it's impossible to say for sure whether the code you're looking at really needs to be written that way or not.

way to define functions in parameters in order to pass one, out of two, functions as an argument

My program had a simple function newline() that would provide an int value to a int variable x.
public void autowordwrap(string wrapthisword)
{
//some code that does things irrelevant to this problem, with the string
x=newline();
//assume x is already declared properly
//do something with value from x
}
Problem started when I introduced a new function sameline()
I want to be able to do any one of these conveniently, at a time:
public void autowordwrap(string wrapthisword)
{
x=newline();
}
or,
public void autowordwrap(string wrapthisword)
{
x=sameline();
}
So, I thought of trying this:
public void autowordwrap(string wrapthisword, Func<void,int> linefunc)
{
x=linefunc;
}
which I can later call on requirement as:
autowordwrap(mystring,newline());
or,
autowordwrap(mystring,sameline());
But it is not quite working out for me!
It says keyword 'void' cannot be used in this context
Problem is:
What I want to do should be simple enough but I'm not quite understanding how it works. I understand that Action<> works for functions without return type and Func<> works for function with a return type.[Reference-1].
What I've gathered so far is:
MSDN tells me: To reference a method that has no parameters and returns void (or in Visual Basic, that is declared as a Sub rather than as a Function), use the Action delegate instead.
Since my newline() function is defined as an int-datatype, and it returns an integer after running, I thought Action<> didn't suit my needs.
This answer has what I need but for the life of me, I couldn't make it work for my specific purpose.
Problem Breakdown
I have two functions newline() and sameline()
I wish to pass any ONE out of the TWO of them as an argument of the function autowordwrap()
which means, in my Main Program, I will be using autowordwrap(somestring, newline()); or autowordwrap(somestring, sameline()); wherever necessary!
both newline() and sameline() are int-datatype functions who return integer value upon being called. For the sake of this problem, lets store it in int x
while trying to solve this, I'm assuming that using Func is used to pass nothing onto the function as an argument while calling example: newline(void) and the int part is used to define the function newline() or any function represented by delegate Func<> as one which returns an int value.
I have realized that what I have seemed to learn must be fundamentally flawed somehow somewhere. Please enlighten me. Reference links would be very helpful too.
Solving this problem in any way is acceptable. You do not need to do this in the way that I may have unintentionally rigidly outlined. Please feel free to explore creative solutions as long as they fulfill the intended purpose in C#
Yes, I acknowledge that THIS is a possible duplicate of this question but I couldn't make much sense of the helpful answer posted over there. Assuming, this will be the case for many future readers, I'm making this question and linking it to that question so that it may be helpful to people who'll face this same problem in the future.
Endnote:
This Question has been solved! The marked answer lays down the way for doing this and there is also some great explanation in the answers. If you are facing some errors while solving a similar question of this nature, you might be able to fix those my looking over screenshots of my own errors. They're here in the revision section no.4
Func<T> has to return some thing, it cannot be void, you have to use Action<T>, if you don't want to return anything.
and if you don't want to pass any input argument to the Func<T>, then you just need one parameter which is return type like:
Func<int> linefunc
you cannot define input type parameter for Func<T,TResult> as void, instead of that just remove the input type parameter of it,
your method definition would look like :
public void autowordwrap(string wrapthisword, Func<int> linefunc)
{
x=linefunc();
}
and call it like:
autowordwrap(mystring, newline);
autowordwrap(mystring, sameline);
You're very nearly there. There are a couple of issues.
First, from your code you appear to be passing the result of your functions in;
autowordwrap("foo", newline());
In this code, C# will invoke the newline function, getting a result. It will then pass the result of that function -- your int -- as the second parameter to autowordwrap.
What you're wanting to do is pass in the un-invoked function itself;
autowordwrap("foo", newline);
So long as the signature of the newline function is compatible with the signature required by autowordwrap, you'll be able to invoke that function inside autowordwrap.
The second part isn't so much the difference between Func<> and Action<>, but about the generic parameters.
The signature you want is a function which takes no parameters and returns an int. So it's reasonable to try
Func<void, int>
but actually, Func<> can take any number of generic types. All but the last are parameters; the last is the return value. So
Func<string, string, int>
corresponds to a method like
public int MyFunction(string s1, string s2) { return 0; }
What you're trying for is a function of no parameters, equivalent to
public int MyFunction() { reutrn 0; }
So the signature you're looking for is
Func<int>
That is, a function of no parameters, returning int. For clarity,
Action<int>
takes one integer parameter and reutrns nothing, equivalent to
public void MyAction(int myParam) { }
--
Oh, and to clarify;
Func<void, int>
Doesn't work because it's equivalent to writing this in C#
public int MyFunction(void x) {}
which is like saying 'a function which takes one parameter, which is a variable of type 'void''. That doesn't make sense, hence the compiler error.
Since your function doesn't need a delegate, it needs an int, you're better off avoiding delegates altogether, just pass the int value, and do this:
public void autowordwrap(string wrapthisword, int separator)
{
//some code that does things irrelevant to this problem, with the string
// if you need it in "x"
x=separator;
//do something with value from x
}
autowordwrap(mystring,newline());
// or
autowordwrap(mystring,sameline());
The general idea for creating clean high quality code is for a function to accept the value(s) it requires to do its specific task, and not some complex input that is "bigger" then that.

Understand specific usage of delegate in C#

According to the MSDN documentation, it says it's useful for following purposes in addition to others which are understandable:
A class may need more than one implementation of the method.
It is desirable to encapsulate a static method.
Can someone help me understand these usages with an example?
A delegate is a reference to a method that you can pass around as an object.
Imagine how useful it could be to have a method that allows its callers to provide part of its own logic. Every caller can have their own method, create a delegate (reference) to their method, and pass it to the method as a parameter. As long as the main method knows what arguments to pass in (if any), it can invoke the method by its reference (delegate).
Here's a simple example, specifically of usage #1 in your question:
void RemoveItem(string item, Action preRemoveLogic)
{
preRemoveLogic(); //we don't know what method this actually points to,
//but we can still call it.
//remove the item
}
void MyCustomLogic()
{
//do something cool
}
/* snip */
RemoveItem("the item", new Action(MyCustomLogic));
//I can pass a reference to a method! Neat!
Delegates are also very important for making events work in .NET.

Using Anonymous method not preferred by .NET itself?

I am trying to learn the anonymous method and tried out this sample.
So, whenever I am trying to call WriteLog function i can just use
()=> { return "someLogData" };
The advantage is that, I need not have a separate function and can save lines of code.
public void WriteLog(Func<string> s)
{
Console.WriteLine(s);
}
But, the same does not work out with functions like
Console.WriteLine( ()=> {return "someString" } );
Why does .NET provide this anonymous method functionality but does not let to use it in one of its own kind?
There are no overloads of Console.WriteLine which accept a Func<string> - and indeed even your current WriteLog method won't do anything useful (as you'd need to invoke the delegate).
Where methods accepting specific delegates do exist - most prominently in LINQ, but elsewhere too - you can indeed use lambda expressions or anonymous methods to invoke them.
You can call Console.WriteLine using a lambda expression, but you need to cast it to a specific delegate type - as there are only conversions from lambda expressions to specific delegate or expression types, not just Delegate or object. So this will work:
Console.WriteLine((Func<string>)(() => "someLogData"));
Again though, it wouldn't be useful to do so anyway - you don't want to log the delegate, you want to log the result of invoking the delegate.
It doesn't make very much sense for Console.WriteLine to accept delegates IMO, but it would make more sense for more flexible logging, where you don't want to evaluate the string unless you're actually going to write it to a log.
Note that your WriteLog call itself can be simpler too:
WriteLog(() => "someLogData");
You need to have your anonymous method executed to get the string.
public void WriteLog(Func<string> s)
{
Console.WriteLine(s());
}
There is an advantage in this form:
public void WriteLog(Func<string> s)
{
Console.WriteLine(s);
}
let's imagine that inside it it's written as:
public void WriteLog(Func<string> s)
{
if (needToWriteLog)
{
Console.WriteLine(s);
}
}
now, let's say that you call it this way:
WriteLog(() => StringThatNeedsFiveSecondsToBuild())
and let's compare it to a similar WriteLog that only accepts a string as a parameter:
WriteLog(StringThatNeedsFiveSecondsToBuild())
The second WriteLog will always spend 5 seconds to build the string, even when needToWriteLog is false, while the first one will build it only if it's true.
Now, compare it to Console.WriteLine: Console.WriteLine will always print its content, so there is no need to use a delegate, because the 5 seconds will always be spent.
There are some methods that are similar in C#, the ones of the family Debug.* and Trace.*, but they are different. You use them like this:
Debug.WriteLine(StringThatNeedsFiveSecondsToBuild())
the trick is that they are defined like this:
[ConditionalAttribute("DEBUG")]
public static void WriteLine(string value)
so that if the DEBUG isn't defined, the whole Debug.WriteLine(StringThatNeedsFiveSecondsToBuild()) is removed, even if it had some side effects (like spending 5 seconds :-) )
All of this show something else: it's VERY important that the delegates you pass to your WriteLog AND the parameters you pass to the Debug.* and Assert.* be side effect free (clearly ignoring the time as a side effect, I mean "real" side effects like changing variable values), because you don't know if they'll really be executed.

C# params keyword with two parameters of the same type

I just encountered something with C# today that I hadn't thought of before. I have two methods in my class, one an overload of the other. They are declared like so:
public void RequirePermissions(params string[] permissions)...
public void RequirePermissions(string message, params string[] permissions)...
In my code, I tried to call the first one like so:
RequirePermissions("Permission1", "Permission2");
...expecting it to call the first overload. Well it called the second overload. The only way I can get it to call the first method in this case is to manually pass a string[] object like so:
RequirePermissions(new string[] { "Permission1", "Permission2" });
Now, this behavior doesn't confuse me because I understand that the compiler can't tell which method I actually wanted to call based on my provided parameters. But was I not careful this could have gone unnoticed in my code. It seems as though Microsoft should have made the compiler throw an error when it encountered a situation like above. Does anyone have any thoughts on this? Is there another way to call the first overload other than the "solution" I posted?
Agreeing with Adam, I'd change it to something like:
public void RequirePermissions(params string[] permissions)
public void RequirePermissionsWithMessage(string message, params string[] permissions)
Personally, I'd do it this way:
public void RequirePermissions(params string[] permissions)...
public void RequireMessageAndPermissions(string message,
params string[] permissions)...
People fall too in love with overloading sometimes, and when you combine that with a love for the params keyword, you just increase the confusion level for whomever eventually has to take over your code.
It looks like there is no other way.
You can find explanation to this behaviour in C# spec http://www.jaggersoft.com/csharp_standard /17.5.1.4.htm and here http://www.jaggersoft.com/csharp_standard/14.4.2.1.htm (paragraph 2)
a parameter array is precisely equivalent to a value parameter (§17.5.1.1) of the same type.
and
The expanded form of a method is available only if the normal form of the method is not
applicable and only if a method with the same signature as the expanded form is not already
declared in the same type
Yes, I agree it should probably be a warning when using variable length argument arrays causes an ambiguous overload - it's very much an edge case, and people almost certainly don't mean to create such situations.
I also don't know of any way, other than that you posted, to avoid the call resolution that occurs - other than to avoid doing it in the first place, which I would highly recommend!
You could not use params and be explicit with your signatures.
public void RequirePermissions(string[] permissions)...
public void RequirePermissions(string message, string[] permissions)..

Categories

Resources