Convert Func delegate to a string - c#

Is there any way to convert an existing Func delegate to a string like that:
Func<int, int> func = (i) => i*2;
string str = someMethod(func); // returns "Func<int, int> func = (i) => i*2"
or at least smth close to it

I found a similar question here. It more or less boils down to:
By #TheCloudlessSky,
Expression<Func<Product, bool>> exp = (x) => (x.Id > 5 && x.Warranty != false);
string expBody = ((LambdaExpression)exp).Body.ToString();
// Gives: ((x.Id > 5) AndAlso (x.Warranty != False))
var paramName = exp.Parameters[0].Name;
var paramTypeName = exp.Parameters[0].Type.Name;
// You could easily add "OrElse" and others...
expBody = expBody.Replace(paramName + ".", paramTypeName + ".")
.Replace("AndAlso", "&&");
Console.WriteLine(expBody);
// Output: ((Product.Id > 5) && (Product.Warranty != False))
It doesn't return the Func<int, int> func = (i) => part like in your question, but it does get the underlying expression!

Related

Is it possible to know what actions have been called on an IEnumerable?

I have several conditions that might affect what filters (.Where(...)) are used on a list. And at some point an exception is thrown, and I would like to know what actions have been called upon the list up until this point.
Is something like this possible?
var myList = new List<SomeClass>();
myList = myList.Where(item => item.property == value);
.
.
.
myList = myList.Where(item => item.otherProperty < otherValue);
Console.WriteLine(myList.ToActionsString());
It might print something like this:
list.Where(i => i.property == <the actual value>)
.Where(i => i.otherProperty < <the actual otherValue>)
Just calling toString() on the list does not exactly give any relevant information, and just listing the items in the list is not of interest.
Warning: there is overhead to this as the compiler has to create all of the Expression objects (which then have to be allocated and compiled at runtime). Use this sparingly.
You can do this using AsQueryable and relying on the ToString logic of the built in EnumerableQuery and Expression classes. The following extension method will convert your query to it's textual representation:
public static string GetText<T>(this IQueryable<T> query) {
retury query.Expression.ToString();
}
It can be used like:
var list = new List<int>();
var query = list.AsQueryable()
.Select((c, i) => c * (i + 1))
.Where(c => c > 5)
.Where(c => c < 10 && c != 7)
.Take(2)
.OrderBy(x => 1);
var text = query.GetText();
This results in the following:
System.Collections.Generic.List`1[System.Int32].Select((c, i) => (c * (i + 1))).Where(c => (c > 5)).Where(c => ((c < 10) AndAlso (c != 7))).Take(2).OrderBy(x => 1)
We can throw an anonymous type in the mix just to see how it looks:
var query = list.AsQueryable()
.Select((c, i) => c * (i + 1))
.Select(x => new { Value = x, ValueSquared = x * x });
var result = query.GetText();
Which will print:
System.Collections.Generic.List`1[System.Int32].Select((c, i) => (c * (i + 1))).Select(x => new <>f__AnonymousType0`2(Value = x, ValueSquared = (x * x)))
Through the use of Expression manipulation, we can make this method a little bit more robust. We can add in line breaks between the method calls and optionally strip off the name of the list's type.
public static string GetText<T>(this IQueryable<T> query, bool lineBreaks, bool noClassName)
{
var text = query.Expression.ToString();
if (!lineBreaks && !noClassName)
return text;
var expression = StripQuotes(query.Expression);
if (!(expression is MethodCallExpression mce))
return text;
if (lineBreaks)
{
var strings = new Stack<string>();
strings.Push(mce.ToString());
while (mce.Arguments.Count > 0 && mce.Arguments[0] is MethodCallExpression me)
{
strings.Push(me.ToString());
mce = me;
}
var sb = new StringBuilder(strings.Pop());
var len = sb.Length;
while (strings.TryPop(out var item))
{
sb.AppendLine().Append(item.Substring(len));
len = item.Length;
}
text = sb.ToString();
}
if (mce.Arguments.Count > 0 && mce.Arguments[0] is ConstantExpression ce)
{
var root = ce.Value.ToString();
if (root != null && text.StartsWith(root))
{
text = noClassName
? text.Substring(root.Length + 1)
: text.Insert(root.Length, Environment.NewLine);
}
}
return text;
}
// helper in case we get an actual Queryable in there
private static Expression StripQuotes(Expression e)
{
while (e.NodeType == ExpressionType.Quote)
e = ((UnaryExpression)e).Operand;
return e;
}
We can call this method as follows:
var list = new List<int>();
var query = list.AsQueryable()
.Select((c, i) => c * (i + 1))
.Where(c => c > 5)
.Where(c => c < 10 && c != 7)
.Take(2)
.OrderBy(x => 1);
var text = query.GetText(true, true);
Which will produce the following:
Select((c, i) => (c * (i + 1)))
.Where(c => (c > 5))
.Where(c => ((c < 10) AndAlso (c != 7)))
.Take(2)
.OrderBy(x => 1)
Note that this is very basic. It's not going to cover the case of closures (passing variables in) you'll get the <>DisplayClass objects written into your query. We can resolve that with an ExpressionVisitor that walks the expression and evaluates the ConstantExpressions representing the closures.
(Unfortunately I do not have time at the moment to provide that ExpressionVisitor solution, but stay tuned for an update)
No, it is not possible, at least not in a direct way as described in your question.
The List is not filtered step by step (i.e., apply Where(expr1) for all elements, then Where(expr2) for all remaining elements, ...), but in a deferred way:
If you request the first item of the resulting IEnumerable, LINQ evaluates the expr1 clause for each item until one item matches.
Then it checks whether this list item also matches expr2.
If it does, return it (or pass to further Where stages).
If it doesn't match, go back to step 1 and continue finding an item which matches expr1.
So logging by simply calling some ToActionsString() is difficult here. As already noted in the comments, it is probably much easier to just log when adding the Where-clauses, since you are in a known state then anyway:
if(condition1)
{
myList = myList.Where(item => item.Property == value);
Log($"Adding expression 1 with value '{value}'");
}
If your concern is that value could change before the IEnumerable is actually evaluated (captured variable), and you cannot restructure your control flow adequately, a workaround may be to create Func<T> objects which output the captured variables, and to evaluate those immediately before iterating the list:
List<Func<int>> values = new List<Func<int>>();
if(condition1)
{
myList = myList.Where(item => item.Property == value);
values.Add(() => value);
}
...
foreach(var v in values)
Log($"List will be filtered by {v()}");
var filteredList = myList.ToList();
Finally, you could call some logging function in your expressions, which logs the conditions and/or catches exceptions when evaluating those conditions:
myList.Where(item =>
{
Log(item, value);
return item.Property == value;
});

How to use ?? with lambda expression?

Can we simplify the following with ?? and lambda expression?
Func<int, int> func = f; // f is a function parameter
if (func == null) // if f passed by the user is null then we use the default identity function f(x)=x.
func = x => x;
I cannot do something Func<int, int> func = f ?? x=>x;. Could you?
Edit
My scenario is as follows.
class Program
{
static double Average(int[] data, Func<int, int> f = null)
{
int sum = 0;
Func<int, int> func = f ?? new Func<int, int>(x => x);
//Func<int, int> func = f;
//if (func == null)
// func = x => x;
foreach (int x in data)
sum += func(x);
return (double)sum / data.Length;
}
static void Main(string[] args)
{
int[] data = { 1, 2, 3 };
Console.WriteLine(Average(data));
}
}
The precedence of null-coalescing operator is higher than lambda declaration.
Func<int, int> func = f ?? (x => x);
Possible duplicate of Null-coalescing operator and lambda expression
This should work
Func<int, int> func = f ?? (x=>x);
Your statement is parsed as
Func<int, int> func = (f ?? x)=>x;
Please search through the available answers first to check if somebody has already answered it.

Func<> add an extra parameter dynamically

how can I add an extra parameter to my Func<> expression ? Something like:
Func<char, bool> myPredicate = (x) => (char.IsLetter(x) || x == 'X');
...
"abc".All(x => char.IsDigit(x) || myPredicate);
but I get an error
Operator '||' cannot be applied to operands of type 'bool' and Func< char, bool>
You need to invoke the myPredicate function like this:
"abc".All(x => char.IsDigit(x) || myPredicate(x));
Or, what about this other approach?
var text = "abc";
var predicates = new Func<char, bool>[] {
x => char.IsLetter(x) || x == 'X',
char.IsDigit
};
var result = predicates.Any(text.All);
// Outputs TRUE
Console.WriteLine(result);
Also, if you need to check many specific characters, you can create a charEquals with curried parameters:
var text = "abc";
// More type inference to generalize charEquals to just equals, please!
Func<char, Func<char, bool>> charEquals = ca => cb => ca == cb;
var predicates = new Func<char, bool>[] {
char.IsLetter,
charEquals('X'),
charEquals('Y'),
charEquals('Z'),
char.IsDigit
};
var result = predicates.Any(text.All);
Console.WriteLine(result);

how to refactor a set of <= , >= if...else statements into a dictionary or something like that

there is a method that receives an int parameter and returns a string by checking parameter through a set of if...else statements :
if(param == 1)
{
return "1";
}
else if(param ==20)
{
return "20";
}
else
{
if(param <= 10)
{
return "below 10";
}
else if(param <= 30 )
{
return "below 30";
}
...
}
I wonder if it is possible to put these ">= , <=" conditions in a dictionary
You can use Dictionary<Func<int, bool>, string>:
private string Do(int input)
{
var dic = new Dictionary<Func<int, bool>, string>
{
{param => param == 1, "1"},
{param => param == 20, "20"},
{param => param <= 10, "below 10"},
{param => param <= 30, "blow 30"}
};
return dic.First(pair => pair.Key(input)).Value;
}
Edit:
Comment from #Maarten is correct, Dictionary does not ensure the order of item, List of KeyValuePair should be the best this case:
private string Do(int input)
{
var pairs = new List<KeyValuePair<Func<int, bool>, string>>
{
{param => param == 1, "1"},
{param => param == 20, "20"},
{param => param <= 10, "below 10"},
{param => param <= 30, "blow 30"}
};
var pair = pairs.FirstOrDefault(pair => pair.Key(input));
if (pair == null) return string.Empty; // return whatever you want
return pair.Value;
}
Can use, for example, a dictionary.
Just an example of pseudocode:
var dic = new Dictionary<int,string>{{1,"1"}, {20, "20"}...}
and instead of long if
string value = null;
dic.TryGetValue(param, out value);
Your operations are not well designed for dictionary. consider the following two case
param == 29 and param ==28
both will give output "below 30". If the range of param variable is large then you have to put all the probable values and there corresponding output strings manually in the dictionary. Is it seems a good idea???
No, Dictionary is not designed for that. If you really got too many comparision conditions you could put the comparatees in an array or list, put the values in another array/list in corresponding order, then you use binarysearch to find the key's index, and use the index to get the value. Here is an example:
static string find(int val)
{
int[] keys = {30,40,50};
string[] messages = {"Below 30","Below 40","Below 50"};
int idx = Array.BinarySearch(keys,val);
if(idx < 0)idx = ~idx;
return idx < 3 ? messages[idx] : "Off the chart!";
}
public static void Main (string[] args)
{
Console.WriteLine (find(28));
Console.WriteLine (find(50));
Console.WriteLine (find(100));
}
I tired the following. Please let us know if you run into any issues with this:
int testInput = 15;
Func<int, bool> delegateForCondition1 = param => param == 1;
var conditionsSet = new HashSet<KeyValuePair<Func<int, bool>, String>>
{
new KeyValuePair<Func<int, bool>, String>(delegateForCondition1, "It is 1"),
new KeyValuePair<Func<int, bool>, String>(param => param <= 10 , "below 10"),
new KeyValuePair<Func<int, bool>, String>(param => param <= 30 , "below 30")
};
foreach (KeyValuePair<Func<int, bool>, String> pair in conditionsSet)
{
Func<int, bool> currentKeyAsDelegate = pair.Key;
bool currentResult = pair.Key(testInput);
Console.WriteLine(currentKeyAsDelegate + "---" + currentResult);
}
//Select the first matching condition
KeyValuePair<Func<int, bool>, String> selectedPair = conditionsSet.FirstOrDefault(p => p.Key(testInput));
if (selectedPair.Key != null)
{
Console.WriteLine(selectedPair.Value);
}
Console.ReadLine();

Any suggest to store a value in a lambda expression

I'm trying to write an in-line function for count occurrences of a word in a string using lambda expressions recursively.
The function:
Func<string, string, int> getOccurrences = null;
getOccurrences = (text, searchTerm) =>
text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) == -1
? 0
: getOccurrences(
text.Substring(
text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase)
+ searchTerm.Length),
searchTerm) + 1;
The problem is that I'm call IndexOf method twice,
The first one is for recursive break condition and the second one is to get the value for add it.
Is there any suggest to call it once?
Thanks in advance.
If you don't mind a non-pure-function lambda you can do:-
Func<string, string, int> getOccurrences = null;
getOccurrences = (text, searchTerm) =>
{
int i = text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase);
return i == -1 ? 0 : getOccurrences(i + searchTerm.Length), searchTerm) + 1;
}
You could do it like this:
Func<string, string, int> getOccurrences =
(text, searchTerm) => getOccurrencesInternal(
text,
searchTerm,
text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase));
Func<string, string, int, int> getOccurrencesInternal = null;
getOccurrences = (text, searchTerm, index) =>
index == -1
? 0
: getOccurrencesInternal(
text.Substring(
index + searchTerm.Length),
searchTerm) + 1;
I suggest you make it a separate method
Func<string, string, int> getOccurrences = GetOccurrences;
private int GetOccurrences(string text, string searchTerm)
{
//...
}
or inline
Func<string, string, int> getOccurrences = delegate(string text, string searchTerm)
{
//...
};
with lambda syntax but just another way of writing the above
Func<string, string, int> getOccurrences = (string text, string searchTerm) =>
{
//...
};
You could use an additional, anonymous lambda and invoke it immediately. I'm not certain of the exact C# syntax, but it should look something like:
Func<string, string, int> getOccurrences = null;
getOccurrences = (text, searchTerm) =>
((index) =>
index == -1
? 0
: getOccurrences(text.Substring(index + searchTerm.Length),
searchTerm) + 1
)(text.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase))
My usual solution to these kind of problems, ignoring any optimizations, is just to remove the matching term and check any change in the resulting string's length.
Func<String, String, Int32> getOccurrences = (text, term) =>
(text.Length - text.Replace(term, "").Length) / term.Length;

Categories

Resources