Could someone please help me to understand how to get all parameters passed to delegate inside delegate itself?
I have class :
public class ShopManager : ShopEntities
{
public ShopManager getWhere(Func<Object, Object> dataList)
{
var x = dataList.???; // how to get arguments?
return this;
}
public Object getLike(Object dataValue)
{
return dataValue;
}
}
Then i call it as :
ShopManager shopManager = new ShopManager()
var demo = shopManager.getWhere(xxx => shopManager.getLike("DATA"));
The question is : how to get passed parameters "xxx" and "DATA" inside method getWhere()?
Thanks in advance.
You can't because it's the other way around. You can't get the arguments because the delegate does not hold them; the getWhere method will need to pass a value for the xxx parameter when invoking the delegate. The anonymous method that the delegate refers to will then receive this value as the xxx parameter, and in turn pass the string "DATA" as argument for the dataValue parameter when calling getLike. The argument values as such are not part of the delegate's state.
If you want to get information about the parameters as such (not their values), you can do that:
// get an array of ParameterInfo objects
var parameters = dataList.Method.GetParameters();
Console.WriteLine(parameters[0].Name); // prints "xxx"
If you use:
public ShopManager getWhere(Expression<Func<Object, Object>> dataList)
then you can divide the Expression into its subexpressions and parse them. But I'm not sure if using a delegate like you do is even the right thing.
You can't do it (easily). But I don't understand your idea. For what reason do you need to look into a dataList? This is just an anonymous method, you can call it and get results, you shouldn't need to examine or modify it at all.
What is your idea? Why not just call shopManager.getLike() ?
you can get the name of function by doing something like below.
var x = dataList.GetInvocationList().FirstOrDefault().Method.GetParameters();
sring name = x.FirstOrDefault().Name
this will print name as 'xxx'
Arguments are what you will provide while invoking the delegate via dataList(args), and not by the recipient of the invocation. If you want to provide additional information to getWhere() , you can try the following ....
public ShopManager getWhere(Func<Object, Object> dataList, params object[] additonalData)
{
// inspect the additionalData
}
Thanks for replies guys, i decided to use Expression> instead of common delegate. This allows to get both sides of expression - LHS and RHS.
For those who are interested in answer, this is it :
http://social.msdn.microsoft.com/Forums/en-US/csharpgeneral/thread/0f6ca823-dbe6-4eb6-9dd4-6ee895fd07b5?prof=required
Thanks for patience and attention.
public static List<object> GetMethodParameterValues(Delegate method)
{
var target = method.Target;
if (target == null) return null;
var fields = target.GetType().GetFields();
var valueList = fields.Select(field => field.GetValue(target)).ToList();
return valueList;
}
Related
I have an XML file with classes name like this:
<ActiveMonitorsList>
<MonitorName>CertificatesMonitor</MonitorName>
<MonitorName>ServicesMonitor</MonitorName>
<MonitorName>LogsMonitor</MonitorName>
<MonitorName>DBMonitor</MonitorName>
</ActiveMonitorsList>
Each of this classes containts a method: bool SingleCheck();
I would like to execute this bool SingleCheck() method for each class that is in this XML file.
What is the best way to do this?
This is what I have so far - it doesn't work:
foreach (string monitorName in monitorsList)
{
Type thisType = GetType();
MethodInfo singleMonitorMethod = thisType.GetMethod("{monitorName}.SingleCheck");
bool methodResult = singleMonitorMethod.Invoke(...);
}
In place of (...) - don't know what to put here, but I want to get
the result of the method (it's always bool).
All of those methods I want to pass as paramters are static.
I guess delegates, Actions or Func<> have to go in here...
Thank You very much in advance!
Edit: Each name in XML points to a separate class. Each class have the same named method: public static bool SingleCheck().
What I want to do is:
get all the monitors names (classes names will be the same)
invoke a method (it has the same name in each class) inside EVERY
class present on that list.
EDIT - PROBLEM SOLVED:
When I first created my project, I included separate folder for all monitors. Then I changed my mind, deleted this folder and added manually SAME FILES to my solution. In this way - those files still had "using <namespace>.Monitors"...
And that's why I couldn't list those classes and the Types were still nulls...
Thanks for all suggestions ! ;)
I would suggest to take this overload of the method Invoke It wants an object(calling instance) and a set of input parameters for the method from you.
Since it is a static method, you can calmly pass null as the first parameter and because you method does not have any parameters you again can calmly pass null as the second value. Don't forget to cast object to the corresponding return type. In your case bool.
bool methodResult = (bool)singleMonitorMethod.Invoke(null, null);
To get the correct Type you actually need to know the namespace! So this would look like this:
foreach (string monitorName in monitorsList)
{
string typeName = $"{yourNameSpace}.{monitorName}";
Type thisType = Type.GetType(typeName);
MethodInfo singleMonitorMethod = thisType.GetMethod("SingleCheck");
bool methodResult = (bool)singleMonitorMethod.Invoke(null, null);
}
If the loop is in the same namespace this should also work:
Type thisType = Type.GetType($"{GetType().Namespace}.{monitorName}");
thisType.GetMethod("{monitorName}.SingleCheck") won't work because of two reasons. 1) You forgot the string interpolation $-sign and thus are searching for a method called "{monitorName}.SingleCheck" which obviously can't exist with such a name. 2) Instead of thisType you need to provide the type containing the method.
Invoke needs to be called with the instance as first parameter - null for static methods - and an object array for the method parameters.
Assuming that your monitor classes are in the same assembly like your current type you would need to do the following:
foreach (string monitorName in monitorsList)
{
Type monitorType = GetType().Assembly.GetExportedTypes().Single(x => x.Name == monitorName);
MethodInfo singleMonitorMethod = monitorType.GetMethod("SingleCheck");
bool methodResult = (bool)singleMonitorMethod.Invoke(null, Array.Empty<object>());
}
I prefer Array.Empty over new object[0] or new object[] { } because it doesn't create a new object every time.
Edited: Changed the type discovery according to Mong Zhu's comment that GetType(monitorName) does need the fully-qualified name.
I'm new to C#. I want to take a list as argument and return another from the data I get from the first one.
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = new List<DestinationGenericMapProps>(datas);
return result;
}
I get this error:
Error 241 The best overloaded method match for System.Collections.Generic.List<VDDataUpdaterGeneric.DataObjects.DestinationGenericMapProps>.List(int) has some invalid arguments
I know this is probably pretty basic but I'm new to C# and struggle with this. Thanks for your help.
List<BoutiqueInWebService> is not a List<DestinationGenericMapProps>.
This will not work unless BoutiqueInWebService is derived from DestinationGenericMapProps.
Basically, there is a List<T>(IEnumerable<T>) constructor, but the T's have to be the same.
Either change your return type to List<BoutiqueInWebService> and change your new statement:
private List<BoutiqueInWebService> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = new List<BoutiqueInWebService>(datas);
return result;
}
or change your parameter to be of type List<DestinationGenericMapProps>:
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<DestinationGenericMapProps> datas)
{
var result = new List<DestinationGenericMapProps>(datas);
return result;
}
Alternatively, if you know how to make a DestinationGenericMapProps from a BoutiqueInWebService, you can use System.Linq and perform a select against the argument:
private List<DestinationGenericMapProps> ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
var result = datas.Select(x => new DestinationGenericMapProps() { ... }).ToList();
return result;
}
Your method return type is a list of DestinationGenericMapProps, but you're trying to create list of BoutiqueInWebService (which is data).
You can do this to match your return type:
private List<DestinationGenericMapProps>
ConstructDestinationMapPropsList(List<BoutiqueInWebService> datas)
{
return (from d in datas
select new DestinationGenericMapProps()
{
// map properties here
Prop1 = d.SomePropInData
}).ToList();
}
You're getting the error because you're trying to populate a list of one type (DestinationGenericMapProps) with objects from a list of a different type (BoutiqueInWebService) which isn't type safe.
You can only do this if BoutiqueInWebService inherits from DestinationGenericMapProps.
C# supports function overloading, which means that a class can have more than one function with the same name as long as the parameters are different. The compiler decides which overload to call by compairing the types of the parameters. This applies to constructors too.
The List class has a three overloads of its constuctor:
List<T>()
List<T>(IEnumerable<T>)
List<T>(int)
I assume that you are trying to use the second of those as it will create a new list from the passed in one. For the list you are creating T is a DestinationGenericMapProps. So the constructors are:
List<DestinationGenericMapProps>()
List<DestinationGenericMapProps>(IEnumerable<DestinationGenericMapProps>)
List<DestinationGenericMapProps>(int)
The list you have passed in has T set to BoutiqueInWebService. As such the compiler is trying to find a constructor like this in the list above.
List<DestinationGenericMapProps>(IEnumerable<BoutiqueInWebService>)
As it can't find one it raises the error you have recieved.
Is it possible to cast a BoutiqueInWebService to a DestinationGenericMapProps object? If so you could do this:
var result = datas.Cast<DestinationGenericMapProps>().ToList()
If no direct cast is possible it may be possible to do a long hand cast like this:
var result = datas.Select(o => new DestinationGenericMapProps() { PropA = o.PropA, PropB = o.PropB /* etc */}).ToList();
I have a method with parameter as object, the object is a string value of properties in DocumentModel class
private PropertyInfo SortingListView(object obj)
{
return typeof(DocumentModel).GetProperty(obj.ToString());
}
I want the PropertyInfo to be used in a lambda expression like below:
var SortedDocuments=Documents.OrderByDescending(x => SortingListView(obj));
But it's not working. Any suggestions? Or any better way? Am I doing it correctly? Please help.
If I got it right, you're trying to sort your list of DocumentModel by whatever property is passed. The way you're currently doing it is wrong because you're actually sorting them by PropertyInfo of your property and since all objects are of the same type this basically does nothing. What you need to do actually is something like this:
private object SortingListView<T>(T obj, string propertyName)
{
return typeof(T).GetProperty(propertyName).GetValue(obj);
}
You can call it this way:
var obj = "SomePropertyName";
var sortedDocuments = Documents.OrderByDescending(x => SortingListView(x, obj));
If you're only going to use it here, you could also do it like this:
var obj = "SomePropertyName";
var sortedDocuments = Documents.OrderByDescending(x =>
typeof(DocumentModel).GetProperty(obj).GetValue(x));
This way you don't need extra method, you have all the logic inside your lambda expression.
Here's my code:
public static Response<TResult> Create<TResult>(Expression<Func<Response<TResult>>> method)
{
var objectMember = Expression.Convert(((MethodCallExpression)method.Body).Arguments[0], typeof(TResult));
var getterLambda = Expression.Lambda<Func<TResult>>(objectMember);
var getter = getterLambda.Compile();
TResult myObject = getter();
// Do something to 'myObject'
// Call original method with updated 'myObject' as the parameter
}
As you can see from the code snippet above, I have a generic method that accepts a method as a parameter.
Orginally, I was calling method.Compile()() which worked fine. However, I now need to update the parameter ("myObject") first before calling the method. So, I've added the lines above to get the parameter.
I then "Do something to 'myObject'". Finally, I want to call the original passed method, but replace the original parameter with the new, updated parameter.
What am I missing?
UPDATE
So, I've, technically, accomplished my need by the following code:
Response<TResult> result = (Response<TResult>)((MethodCallExpression)method.Body).Method.Invoke(parentType, new object[] { myObject });
But this is using a lot of reflection and casting. Is there a better way to do this?
Thanks,
Joshua
This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How can I pass an anonymous type to a method?
I have the following LINQ Statement, whose output has to be processed in another method:
var data = from lines in File.ReadAllLines(TrainingDataFile)
.Skip(ContainsHeader ? 1 : 0)
let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
let target = f[TargetVariablePositionZeroBased]
select new { F=f, T=target };
What should be the datatype of the parameter in the method that will take this data?
You can not return the anonymous data types from a method. You can define a class and return object of that class from query and pass it to target method.
public class SomeClass
{
public string F {get; set;}
public string T {get; set;}
}
var data = from lines in File.ReadAllLines(TrainingDataFile)
.Skip(ContainsHeader ? 1 : 0)
let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
let target = f[TargetVariablePositionZeroBased]
select new SomeClass { F=f, T=target };
You can pass the query result IEnumerable<SomeClass> to method as parameter.
public void MethodToCall(IEnumerable<SomeClass> someClass)
{
}
To call the method by passing the query result (IEnumerable<SomeClass>) that is stored in data in this sample code
MethodToCall(data);
You can't very easily pass anonymous types around. You can either create a class, or since your data has only two properties, use a Tuple:
select new Tuple<List<string>, string> (f, target);
If I have the data types correct, then the data type of the parameter would be:
IEnumerable<Tuple<List<string>, string>>
and you would reference F and T using the Tuple properties Item1 and Item2.
1) Just to pass the result of the query, make your function generic, that will do:
var data = from lines in File.ReadAllLines(TrainingDataFile)
.Skip(ContainsHeader ? 1 : 0)
let f = lines.Split(new[] { FieldSeparator }).ToList<String>()
let target = f[TargetVariablePositionZeroBased]
select new { F=f, T=target };
SomeMethod(data);
public void SomeMethod<T>(IEnumerable<T> enumerable)
{
// ^^choose the return type..
}
Simple. If the processing inside the method is something so simple this will do. But you won't be able to access properties F and T inside the method.
To do so:
2) You can use the "cast by example" trick shown here by Eric. To quote him:
We use method type inference and local variable type inference to tell
the compiler "these two things are the same type". This lets you
export an anonymous type as object and cast it back to anonymous type.
...the trick only works if the example and the source objects were
created in code in the same assembly; two "identical" anonymous types
in two different assemblies do not unify to be the same type.
SomeMethod(data);
public void SomeMethod(IEnumerable<object> enumerable)
{
var template = new { F = new List<string>(), T = string.Empty };
foreach (var item in enumerable)
{
var anonymousType = item.CastToTypeOf(template);
//print string.Join(", ", anonymousType.F) + " - " + anonymousType.T //compiles
//or whatever
}
}
//a more generic name perhaps is 'CastToTypeOf' as an extension method
public static T CastToTypeOf<T>(this object source, T example) where T : class
{
return (T)source;
}
The catch here is that SomeMethod now is tailor made for your anonymous type, since you're specifying a specific type inside the method, so its better to not make the function generic (though you can do) and to give a suitable name for the function.
3) If function is just for your unique type now, I would better have them all wrapped in a single method and not pass at all - no hassle! :)
4) Or you can delegate the action to be done on your anonymous type. So method signature would be like:
SomeMethod(data, d => print string.Join(", ", d.F) + " - " + d.T);
public void SomeMethod<T>(IEnumerable<T> enumerable, Action<T> actor)
{
foreach (var item in enumerable)
actor(item);
}
If it matters you can have Func delegate as well by having one more type argument.
5) Rely on fiddly reflection to get the properties from your anonymous type otherwise.
6) Use dynamic keyword on method argument and now you have dynamic typing. Both the above doesnt give you benefits of static typing.
7) You will be better off having a separate class that holds F and T. And that the best of all. But ask yourself do they together represent something as an entity?
8) If not, just pass an IEnumerable<Tuple> or IDictionary depending on what matters.
It all depends on what/how you want to achieve with the method. Personally, I would go for the approach 2 in a hobby project (for the fun involved), but in production code 3, 4, 7, 8 depending on the context.