C# delegate properties - c#

Is it possible to pass a property as a delegate in c#?
I know that in C# we can pass functions as arguments to a method, But I have a property which in my situation is not much different than a function that takes no parameters, how can I pass it as an argument?
public IWebElement Name => _element.FindElements(By.ClassName("field-key"))[0].FindElements(By.TagName("label"))[0];
I'm working with selenium and changing elements. That's the reason why my Name property is the way it is. So I always get the latest name when I do something like: MultinationalPage.GeneralPanel.DefinitionField.Name.
In my test to prevent getting Stale Element Reference Exception, I use Thread.Sleep(), I know there are better ways, however, for now, this is my approach.
Thread.Sleep(5000)
multinational.EditModal.StateTracker.VerifyChanges(_multinational.GeneralPanel.DefinitionField.Name);
I would like to do the Thread.Sleep(5000) inside the VerifyChanges() that's why I want to pass the Name property as a delegate to VerifyChanges(MultinationalPage.GeneralPanel.DefinitionField.Name).
Something like this:
public void VerifyChanges(Func<IWebElement> Name)
{
Thread.Sleep(5000)
var name = Name;
...
}

I think there is a better way to solve this problem.
Create an interface:
interface IHasName
{
IWebElement Name { get; }
}
Implement:
class YourClass : IHasName
{
public IWebElement Name
=> _element.FindElements(By.ClassName("field-key"))[0].FindElements(By.TagName("label"))[0];
}
Then your method:
public void VerifyChanges(IHasName hasName)
{
Thread.Sleep(5000)
var name = hasName.Name;
...
}

A solution is to encapsulate the access to property in a lambda :
multinational.EditModal.StateTracker.VerifyChanges(
() => _multinational.GeneralPanel.DefinitionField.Name
);

If you can share your full code then we can have a better understanding of the problem that you are trying to solve. Passing a property is the same as passing a parameter value to a function. e.g. Lets us assume your function looks like
public void MyFunction(IWebElement webElement)
{
// Your code goes here.
}
You can call this function and pass the property as follows:
MyClass obj = new MyClass();
MyFunction(obj.Name);
I assumed that MyClass is the name of the class which has this property.
======== Edited
In you case you can wrap your code inside a Func as follows
public void VerifyChanges(() => multinational.GeneralPanel.DefinitionField.Name);

Related

Can I take a method identifier (ex: MyClass.MyMethod) as a method parameter in C#?

The way nameof() works, I can write
var s = nameof(HomeController.Index);
Can I make my own compile-time method that works the same way and can take the same input? For example:
public static string MyMethod(Something input) // I'm not sure what Something should be
{
// do something with input to get method info
}
... // elsewhere in code
var s = MyMethod(HomeController.Index);
Update for context:
More specifically I would like to be able to make a helper method to be used in a Razor view. For example, I might call MyMethod(HomeController.Index) to return a string listing the controller name and the action name. It would be nice to be able to make such a method without having to pass both the controller type HomeController and the method name Invoke as separate parameters.
Update for more context and example:
My goal is to avoid magic strings when specifying controllers and actions in Razor views. Here's an example of how I am doing this currently by checking for the [Action] attribute on actions and trimming of the "Controller" suffix from controllers. But you can see that it's verbose.
<a asp-action="#(ControllerHelpers.GetActionName<HomeController>(nameof(HomeController.Index)))" asp-controller="#(ControllerHelpers.GetRouteName<HomeController>())">Link to Home</a>
I'm looking for a way to do something like this
<a asp-action="#ControllerHelpers.GetActionName(HomeController.Index)" asp-controller="#(ControllerHelpers.GetRouteName<HomeController>())">Link to Home</a>
and perhaps eventually my own tag helper like this. But even here I'd like to avoid having to separately pass both the controller and the action name (just for concision).
<a asp-controller-action="HomeController.Index">Link to Home</a>
You can do this via reflection, passing the method name as a string, and then using Type.GetMethod to get the method and then call Invoke on that, with the type instance.
However, the better thing to do here is to use a delegate. Specifically, you can do something like:
public static string MyMethod(Func<IActionResult> func)
And then:
var s = MyMethod(() => controller.Index());
Inside MyMethod, you'd invoke this like any other method, i.e. func().
That said, what you're trying to ultimately achieve here is unclear and suspect. You can't just invoke HomeController.Index; you need a HomeController instance. Manually newing up a controller, is pretty much always wrong, so there's probably a better way to achieve what you want, in general.
In other words, you seem to have an XY problem here. You're trying to do X, and you've decided Y is the way to do that (here, trying to pass a method reference and invoke that for some reason). But, you don't know how to do Y, either. Instead of asking about X, i.e. the thing you actually need help with, you're asking about Y, which almost assuredly isn't even a good way to do X, in the first place. Give us some more info on X, the thing you actually want, and we can probably give you a better method to achieve that.
I'm not 100% sure what you're asking for, but here's an example of how to do something like what you're asking for using delegates (MS delegate guide):
class MethodRunner
{
// use delegates to define the method signature that you'll operate on
public delegate void NoArgFormat();
public delegate void OneStringArgFormat(String arg);
//You can accept delegates as function arguments, then call them
//with a "live" object instance
public void RunMyMehtod(NoArgFormat methodToRun)
{
methodToRun();//runs the methd passed in
}
public void RunMyStringArgMethod(OneStringArgFormat methodToRun, String arg)
{
methodToRun(arg);
}
}
class Program
{
//This matches to "NoArgFormat" delegate definition
public void Method1()
{
Console.WriteLine("Method1");
}
//This matches the OneStringArgFormat
public void Method2(String arg)
{
Console.WriteLine(arg);
}
static void Main(string[] args)
{
Program p = new Program();
MethodRunner mr = new MethodRunner();
mr.RunMyMehtod(p.Method1);
mr.RunMyStringArgMethod(p.Method2, "First");
mr.RunMyStringArgMethod(p.Method2, "Second");
}
}
Sample output:
C:\Workspace\SampleApp\bin\Debug>SampleApp.exe
Method1
First
Second

Pass member name as argument

Is possible pass only the name of method / function (member) without parameters?
Something similar to what Moq does:
Mock<Foo>fooMoq = new Mock<Foo>();
fooMoq.Setup(f => f.DummyMethod(It.IsAny<string>()));
but, without It.IsAny ():
Mock<Foo> fooMoq = new Mock<Foo>();
fooMoq.Setup(f => f.DummyMethod);
I don't mean in the context of Moq, but in general.
Thanks.
Edit:
I want to obtain is the name of the member, to later intercept calls to that method. That's why I'm not interested in knowing what parameters will be passed to the method, I just want to know what methods they want to intercept.
No you can't pass method from an instance of lambda parameter. The closest you can get is to use method group conversion. However you didn't really specify what you want to achieve.
After OP edit:
There is no easier way to do it, that's why popular frameworks like MOQ requires from you to pass all required parameters. One of common ways of doing this is just pass a method with only nulls (or default values for value types) like this:
fooMoq.Setup(f => f.DummyMethod(null,null,null));
There is also one more approach which allow you to not pass any parameters but you lose static type check for the type of a method so you can pass any method name there. Also reading it will be more problematic because you'll only have method name and type which can cause issues when reading overloaded functions.
public class Program
{
public static void Main()
{
Setup<Test>(nameof(Test.DummyMethod));
}
public static void Setup<T>(string methodName)
{
}
}
public class Test
{
public void DummyMethod()
{
}
}

How to get the methodname from a known method?

Is it possible to get the name of another method in the same class but without using a manually written string?
class MyClass {
private void doThis()
{
// Wanted something like this
print(otherMethod.name.ToString());
}
private void otherMethod()
{
}
}
You may ask why: well the reason is that I must invoke the method later on like this Invoke("otherMethod"), however I don't want to hardcode this string myself as I can't refactor it anymore within the project.
One approach is you can wrap it into delegate Action, then you can access the name of method:
string name = new Action(otherMethod).Method.Name;
You can use reflection (example - http://www.csharp-examples.net/get-method-names/) to get the method names. You can then look for the method that you're looking for by name, parameters or even use an attribute to tag it.
But the real question is - are you sure this is what you need? This looks as if you don't really need reflection, but need to think over your design. If you already know what method you're going to invoke, why do you need the name? How about a using a delegate? Or exposing the method via an interface and storing a reference to some class implementing it?
Try this:
MethodInfo method = this.GetType().GetMethod("otherMethod");
object result = method.Invoke(this, new object[] { });
Btw. I also found (in the expansions of the internet) an alternative solution for only getting the string of a method. It also works with parameters and return types:
System.Func<float, string> sysFunc = this.MyFunction;
string s = sysFunc.Method.Name; // prints "MyFunction"
public string MyFunction(float number)
{
return "hello world";
}

How to know if an Action is an anonymous method or not?

This is C# 4.0.
I have a class that stores WeakReferences on some Actions like that:
public class LoremIpsum
{
private Dictionary<Type, List<WeakReference>> references = new Dictionary<Type, List<WeakReference>>();
public void KeepReference<T>(Action<T> action)
{
if (this.references.ContainsKey(typeof(T)))
{
this.references[typeof(T)].Add(new WeakReference(action));
}
else
{
this.references.Add(typeof(T), new List<WeakReference> { new WeakReference(action) });
}
}
}
This class has another method allowing to execute the Actions passed to it later but it's of little importance in this question.
and I consume this class this way:
public class Foobar
{
public Bar Bar { get; set; }
public void Foo(LoremIpsum ipsum)
{
ipsum.KeepReference<Bar>((b) => { this.Bar = b; });
ipsum.KeepReference<Bar>(this.Whatever);
}
public void Whatever(Bar bar)
{
// Do anything, for example...:
this.Bar = bar
}
}
Bar being a third class in my application.
My question:
In the KeepReference method, how can I know if the Action passed in parameter refers to an anonymous method (this.Bar = b;) or a concrete method (this.Whatever)?
I checked the properties of action. I couldn't find any property on action (like IsAbstract) of a IsAnonymous kind. The underlying type is MethodInfo which makes sense because after compiling I can see in ildasm the anonymous method "became" a normal method on Foobar. In ildasm I can also see that the anonymous method is not a full pink square but a white square surrounded by pink and in its definition there's a call to some CompilerServices classes but I don't know how to take advantage of this back in C#. I'm sure it's possible to get to know about the real nature of action. What am I missing?
For the sake of having an "accepted" answer on this question, I went as per the link given by Michael Kjörling in his first comment on my question.
if (action.Target.GetType().GetMethods().Where(method => method.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()).Contains(action.Method))
{
// ...
}
Compiler generated methods will always have their names with angled brackets like below
Void <Main>b__0()
so why not just fetch the name and see if it has angled brackets in it.
Action someaction = () => Console.Write("test");
string methodName= RuntimeReflectionExtensions.GetMethodInfo(someaction).ToString();
if(methodName.Contains("<"))
Console.write("anonymous");
or you can use much better pattern matching with regex

Get Name of Action/Func Delegate

I have a weird situation where I need to get the Name of the delegate as a string. I have a generic method that looks like this.
private T Get<T>(T task, Action<T> method) where T : class
{
string methodName = method.Method.Name //Should return Bark
}
and I am calling it like this
private void MakeDogBark()
{
dog = Get(dog, x=>x.Bark());
}
But instead of seeing "Bark" I see this "<MakeDogBark>b__19". So it looks like it is giving me the method name that made the initial call instead of the name of the delegate.
Anyone know how to do this?
It's giving you the name of the method which is the action of the delegate. That just happens to be implemented using a lambda expression.
You've currently got a delegate which in turn calls Bark. If you want to use Bark directly, you'll need to create an open delegate for the Bark method, which may not be terribly straightforward. That's assuming you actually want to call it. If you don't need to call it, or you know that it will be called on the first argument anyway, you could use:
private T Get<T>(T task, Action method) where T : class
{
string methodName = method.Method.Name //Should return Bark
}
private void MakeDogBark()
{
dog = Get(dog, dog.Bark);
}
You could get round this by making the parameter an expression tree instead of a delegate, but then it would only work if the lambda expression were just a method call anyway.
You can get the name of the method call by making the parameter an expression instead of a delegate, just like Jon mentioned
private T Get<T>(T task, Expression<Action<T>> method) where T : class
{
if (method.Body.NodeType == ExpressionType.Call)
{
var info = (MethodCallExpression)method.Body;
var name = info.Method.Name; // Will return "Bark"
}
//.....
}

Categories

Resources