I'd like to write a method that takes a string parameter -- say, sLastName -- and returns a very simple Func<string> that has captured that variable and simply returns it. This doesn't work...
Func<string> MyMethod (string myStringVar)
{
return () => myStringVar;
}
...in that if sLastName is "Smith", and I call MyMethod(sLastName).Invoke(), I'll get back "Smith"; but if I then set sLastName to "Jones" and call MyMethod again, I'll still get back "Smith".
So, to make using MyMethod as simple as possible, I'd like to be able to pass sLastName in as a string, and then (assuming sLastName has been declared, is in scope, etc.), by somehow leveraging the Expression class, return a Func<string> with sLastName embedded as a captured variable... like so...
MyMethod("sLastName").Invoke()
...and it would return the current value of sLastName. But I can't see any way of using strings to build Expressions, and haven't yet found such in StackOverflow. Any way to do this?
Thanks in advance! (By the way, I know I can just create the lambda on the fly and pass it in as a parameter; just looking for something even simpler, where I can use only the name of the variable.)
What you really want is for myStringVar to be declared as a ref variable in MyMethod so that it's passed by reference instead of by value (which will copy the value and is why you don't see the updated value). That way the lamda has a reference to myStringVar and will thus return the latest value whenever called. However, ref variables aren't allowed to be referenced in lambda functions.
In your code, you're passing by value:
Func<string> MyMethod (string myStringVar)
{
return () => myStringVar; // When MyMethod is called, the value of myStringVar inside this function and thus the lambda is a copy of what was passed in
}
This would be similar, but without knowing what you're trying to accomplish I'm not sure if it will work for your scenario:
public static void Main()
{
var myStringVar = "Smith";
Func<string> returnMyString = () => MyMethod(ref myStringVar);
Console.WriteLine(returnMyString());
myStringVar = "Jones";
Console.WriteLine(returnMyString());
}
public static string MyMethod(ref string myStringVar)
{
return myStringVar; // myStringVar holds a reference to the one from the Main program and so returns the latest value
}
The output is:
Smith
Jones
Here's a .net fiddle with the example
Related
I have a delegate that I define at runtime. I want it to check if a dictionary contains a particular key. One of the delegate parameters is a ref to the dictionary, so the dictionary is always the latest version (not a frozen copy).
However the key to lookup is an object field, but I don't want to pass it a ref to that object. I just want to grab the field value and use it as if it were a constant (frozen copy).
This is the exact code snippet. I pass in a ref to the netController, which allows me to check something is there using ContainsID(). The ID parameter comes from the msg object, but I just want to use the value without the reference to the container object.
Func<netController, bool> resolveFunc = delegate (netController nc)
{
return nc.ObjectRegister.ContainsID(msg.parentObjectID);
};
nc is in scope in this delegate, but msg is not. Is this do-able, or does everything need to be passed in as a parameter?
Note: I say dictionary, but its actually a dictionary-like object. ContainsID() is a wrapper for ContainsKey(). Don't worry about this part.
This is called an implicit capture of a variable by delegate, and it is definitely possible.
As long as msg is in scope outside the line that creates your delegate, C# would capture it, and let you use it inside your code:
Func<Controller,bool> MakeChecker(Message msg) {
return nc => nc.ObjectRegister.ContainsID(msg.parentObjectID);
}
Above method captures msg implicitly from the context that creates the delegate, rather than passing it as a delegate parameter. I used lambda syntax in place of anonymous delegate syntax:
Func<Controller,bool> MakeChecker(Message msg) {
return delegate (netController nc) {
return nc.ObjectRegister.ContainsID(msg.parentObjectID);
}
}
How do I store this into a delegate?
Like this:
Message msg = ...
Func<Controller,bool> myChecker = MakeChecker(msg);
I was reading about Web API 2 and Entity Framework where I bumped across this line:
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
I looked up the => operator and found that it has something to do with lambdas, although I'm still not sure exactly how it fits in or what's going on here. Can anybody help me out? What's going on in this line?
this.Database.Log = s => System.Diagnostics.Debug.WriteLine(s);
Basically means:
this.Database.Log = blah;
void blah(string s)
{
System.Diagnostics.Debug.WriteLine(s);
}
Database.Log is of the type Action<T>, which is a delegate type
See the property on MSDN
See Action<T> on MSDN
See delegates on MSDN
See lamdas on MSDN
So when this.Database wants to log stuff, it'll call it's "Log" property which is an Action<string>. Since it's an Action<string> (and more importantly, Action<T> is a delegate type), you can assign either a method which returns no value and has a single parameter (which is a string) or a lamda with a single parameter (also, string) (and no return value).
The Database.Log property is of type Action<string>. That means it's a delegate that takes a string and has no return value (i.e. its return type is void). So this line assigns the to the Log property an anonymous function which accepts a string value and writes it to the debugger window.
Because you're assigning the lambda expression to a property with a known delegate type, the compiler can infer the type of the argument (s) and return type from the context. This is shorthand for
this.Database.Log = (delegate (string s){ System.Diagnostics.Debug.WriteLine(s); });
Which in turn is short hand for (as the other answers mention) creating a private method and assigning a delegate reference to that private method:
this.Database.Log = (Action<string>)__compiler_generated_function__;
private void __compiler_generated_function__(string s) {
System.Diagnostics.Debug.WriteLine(s);
}
It means assign a function to this.Database.Log. When this.Database.Log executes it finds the function
s => System.Diagnostics.Debug.WriteLine(s);
That stands for
void LogMethod(string s)
{
System.Diagnostics.Debug.WriteLine(s);
}
I have the following code
class Program
{
static void Main()
{
A a = new A();
a.M(null);
}
}
class A
{
public void M(int? i)
{ }
public void M(string s)
{ }
}
And I have an error, because the call is ambiguous. I need to change the call of M method without adding any lines to Main method and accessing class A so that it became correct. Could someone please tell me how to do this?
You can use explicit cast:
A a = new A();
a.M((string)null);
or
a.M((int?)null);
to help the compiler of picking the right overload. Note that C# compiler can't determine what method overload to call based on null literal.
For advanced topic consider Eric's article What is the type of the null literal?
edit:
since your argument names are different, you can use named arguments, which are avaliable since C# 4.0:
a.M(i : null);
or
a.M(s : null);
You can use a cast, or the default keyword, or for int?, new int?() (which, due to how Nullable types work, is also the same as null). You could also use named parameters to disambiguate. Or, of course, if you were ok with adding another line, you could declare your value in a variable and pass that in.
// these call the int? overload
a.M(default(int?));
a.M((int?)null);
a.M(new int?());
a.M(i: null);
int? i = null;
a.M(i);
// these call the string overload
a.M(default(string));
a.M((string)null);
a.M(s: null);
string s = null;
a.M(s);
The answer is that you cannot overload the member M this way if you cannot alter existing call sites.
Presumably you are adding one of the two methods and can alter the call sites for calls to the new method. Change the name of the method for those new call sites to use.
string thing = "etc";
thing = thing.GetName();
//now thing == "thing"
Is this even possible?
public static string GetName(this object obj)
{
return ... POOF! //should == "thing"
}
I agree #Reed's answer. However, if you REALLY want to achieve this functionality, you could make this work:
string thing = "etc";
thing = new{thing}.GetName();
The GetName extension method would simply use reflection to grab the name of the first property from the anonymous object.
The only other way would be to use a Lambda Expression, but the code would definitely be much more complicated.
No. At the point you're using it, the "name" would be "obj" - This could be retrieved (with debugging symbols in place) via MethodBase.GetCurrentMethod().GetParameters()[0].Name.
However, you can't retrieve the variable name from the calling method.
If you need the original variable name inside an extension method, I think it's best to do this:
thing.DoSomething(nameof(thing));
public static string DoSomething(this object obj, string name) {
// name == "thing"
}
New in C# 6 is nameof() which would replace the extension method entirely.
if (x == null) throw new ArgumentNullException(nameof(x));
WriteLine(nameof(person.Address.ZipCode)); // prints "ZipCode”
Somewhat related is the CallerMemberAttribute which will get the name of the method where the function was called. A useful comparison of the two methods, with examples relating to PropertyChanged events, also talks about the IL code generated (TL;DR: they're the same).
I have a function that wraps a call to one of my socket types. If there is an error, I want to be able to print a warning and retry. In the warning, I want to have the method name. However, it was declared as a lambda. Is this even possible?
How I call the function (assume in function called myMain):
SafeSocketCommand(() => this.mySocket.ReadCurrentBuffer());
Basic wrapping function:
protected TResult SafeSocketCommand<TResult>(Func<TResult> socketCommand)
{
TResult retValue = default(TResult);
try
{
retValue = socketCommand();
}
catch (PacketLost)
{
ReportToLogs("Timeout on command '" + socketCommand.Method.Name);
}
return retValue;
}
But socketCommand.Method.Name gives me the calling method (from the Stack Trace?) '< myMain >b__3' and I want the actual function being invoked by socketCommand (mySocket.ReadCurrentBuffer). Is it possible to get this information anywhere, or is it lost due to declaring in a lambda?
EDIT:
I should have mentioned that I use this particular calling convention so that I can use socket based commands of various signatures.
int i = SafeSocketCommand(() => this.mySocket.FunctionReturnsInt())
bool b = SafeSocketCommand(() => this.mySocket.FunctionReturnsBool(string s))
object o = SafeSocketCommand(() => this.mySocket.Complicated(string s, int i, bool b))
It also handles no return type signatures by overloading:
protected void SafeSocketCommand(Action socketCommand)
{
SafeSocketCommand(() => { socketCommand(); return 0; });
}
If you modify your SafeSocketCommand to accept an Expression<Func<TResult>> then you'll get access to an expression tree that represents the body of the lambda, from which you can access the ReadCurrentBuffer call directly.
However, if you do this, you're no longer dealing with a regular anonymous method; to actually call it you'll need to compile the expression tree to code. You may also need to be flexible as to what your code expects to appear inside the lambda's body.
No, because lambda's don't have names; they're anonymous functions. You could get the method name from the last stackframe, though:
new StackFrame(1).GetMethod().Name;
Func<TResult> is just a delegate. Rather than use a lambda, create a method that matches the signature of Func<TResult> and call that. That way, you'll have whatever name you want.
SafeSocketCommand(MyNewMethod);
...
public TResult MyNewMethod()
{
return this.mySocket.ReadCurrentBuffer();
}
In this case, you can simply this call instead. It'll be faster and smaller generated code too.
SafeSocketCommand(mySocket.ReadCurrentBuffer);
In general, the StackTrace of the Exception object contains the full information you are looking for, much more accurately than printing the method name, or you can use the TargetSite property for the name of the method that threw the exception.