1

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:

  1. 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.
  2. 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

Community
  • 1
  • 1
Siddhant Rimal
  • 975
  • 2
  • 11
  • 32
  • I've answered this for you -- remove the parens after, say, newline() -- that will pass the newline function into your function. What you're doing is passing an integer in and it's expecting a method that, when invoked, returns an integer. – Steve Cooper May 01 '16 at 19:28
  • Your edit is very much **against** how you should be asking questions. It's extremely long, it's extremely verbose, it has a ton of unneeded pictures (use code blocks if these are relevant at all) and what's worse is that your original question has none of these issues. Read [ask] and rollback your post. – Amit May 01 '16 at 19:28
  • @Amit : As I've mentioned in the question, the screenshots are a response to a request made in the comments. Somebody asked them because they were not getting a clear picture of my problem. So I simply did as they requested. There are **no** unneeded pictures. Error list is necessary to pin point the problem. My original question was based on my assumed solution. Its an outline and hence does not encounter the same errors that were faced when I applied some of the suggestions. I think it is needed if someone wants to understand the question and hence I justify my actions. Have a good day! – Siddhant Rimal May 02 '16 at 07:24

3 Answers3

3

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);
Ehsan Sajjad
  • 61,834
  • 16
  • 105
  • 160
  • I want it to return something. I want it to return the value of int that newline() or sameline() would return. I dont necessarily need it outside of autowordwrap() however. I did this assuming func<> defines the function it is operating on. Both my functions newline() and sameline() need no argument(hence void) but give back an int value. How do I go on about this? I'm confused – Siddhant Rimal May 01 '16 at 15:14
  • see my updated post, you need to just pass then one type argument which is the TResult – Ehsan Sajjad May 01 '16 at 15:15
  • Following up on your recent Edit, now I get an error message saying "cannot convert from 'int' to 'System.Func'. Could you enlighten me about the conversion type here? – Siddhant Rimal May 01 '16 at 15:16
  • would I need to change how I have declared newline() too? Because I'm still getting errors. I've currently declared it as: **public void newline(){//stuff}** Does that need change? Errors are:"cannot convert from 'System.Func' to 'int'" | "Cannot convert lambda expression to delegate type 'System.Func' because some of the return types in the block are not implicitly convertible to the delegate return type." | "Cannot convert method group 'newline' to non-delegate type 'int'. Did you intend to invoke the method?" | **which leads me to believe a change is necessary?** – Siddhant Rimal May 01 '16 at 15:27
  • your newline and sameline needs to return an int to match the delegate signatures as delegate return type is also int – Ehsan Sajjad May 01 '16 at 15:29
  • it cannot be void, if you want it to void,then you have to use ``Action`` – Ehsan Sajjad May 01 '16 at 15:35
  • if it returns void what is the purpose of :``x=linefunc();`` then? – Ehsan Sajjad May 01 '16 at 15:37
  • ah please pardon the confusion. I meant to type public void autowordwrap(string, function){//stuff} and public int newline(){//stuff} – Siddhant Rimal May 01 '16 at 17:43
  • then the above way with ``Func`` should work for you – Ehsan Sajjad May 01 '16 at 17:46
  • well it simply didn't. I got the exact opposite error message which said "cannot convert from 'System.Func' to 'int'" – Siddhant Rimal May 01 '16 at 17:47
  • please edit your question and show updated changes what you did – Ehsan Sajjad May 01 '16 at 17:48
  • Edited my Question to provide all relevant screenshots. Please have a look. I can provide complete code if that was insufficient information – Siddhant Rimal May 01 '16 at 18:44
  • you are calling it wrong, instead of ``linefunc`` you have to pass ``newline`` or ``sameline`` as input to your wrap method – Ehsan Sajjad May 01 '16 at 18:46
  • I know what you're talking about already. Again, sorry for the inconvenience! I have been awake for too long and made some mistakes. I have updated the screenshots and descriptions at the end once again. please have a look. – Siddhant Rimal May 01 '16 at 19:09
  • you have to remove the **()** of newline method when passing to wrap method as it will be invoked from inside the wrap method where you are instantiating ``new Point(pagemarginX,linefunc())`` – Ehsan Sajjad May 01 '16 at 19:18
3

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.

Steve Cooper
  • 20,542
  • 15
  • 71
  • 88
  • 1
    Thanks for the well rounded explanation. +1. I am writing a walkthough comment based on how it worked for me. Solved by removing the parenthesis from **newline()** while passing as an argument. It is also important to add parenthesis to **linefunc** when in use. ***Example:*** function is called as **autowordwrap(mystring,newline)** and function is declared and delegate used as **public void autowordwrap(string mystring, Func linefunc){x=linefunc();}** – Siddhant Rimal May 02 '16 at 07:36
1

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.

Amit
  • 45,440
  • 9
  • 78
  • 110
  • huh, unsurprisingly it works without hitches! I wonder how such an elegant solution slipped my mind. Well done! It solves the problem and +1 for that. But I would also like to learn about delegates and for that reason I shall reserve the marked andswer for the best answer that solves it for any function that can be actually passed. Props and +1 for simplicity and creative answer :) – Siddhant Rimal May 01 '16 at 18:48