working with reflection and stream reader in c sharp - c#

i have a question on reflection in c sharp..this is my question
1) define class MyClass with different fields with different accessors(private, public, protected) also methods with different argument set and return types
2) define MyTestClass which contains method, that do the following: print method names for spicified class name, where methods contain string arguments, class name is string value. Call some method of class, and put arguments to method, arguments should read from text file(name of class and name of method is arguments of method
i have answered the question but i am having a problem,i can not read from the text file when i run the program inside the text file there is a double number and an integer but its not showing on the console
this is my code
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.IO;
class MyClass
{
private int i;
public double d;
private string s;
public bool b;
public MyClass()
{
i = 1;
d = 0.1;
s = "1";
b = true;
}
public void Method0()
{
Console.WriteLine("Method with no arguments, no return value.");
}
private int Method1(int arg0)
{
Console.WriteLine("The method returns int, int gets.");
return arg0;
}
private double Method2(int arg0, double arg1)
{
Console.WriteLine("Method returns a double, taking int and double.");
return arg1 * arg0;
}
public bool Method3(string arg0)
{
Console.WriteLine("Method returns a bool, accepts string");
return arg0.Length>10;
}
public bool Method3(string arg0,string arg1)
{
Console.WriteLine("The method takes two arguments string.");
return arg0 == arg1;
}
public static char Method4(string arg0)
{
Console.WriteLine("Method returns a char, accepts string. .");
return arg0[1];
}
public void Method5(int arg0, double arg1)
{
Console.WriteLine("arg1 = {0} arg2 = {1}.",arg0,arg1);
}
}
class MyTestClass
{
public static string[] GetMethodsWithStrParams(string className)
{
var t = Type.GetType(className);
List<string> res = new List<string>();
foreach (var method in t.GetMethods())
{
foreach (var param in method.GetParameters())
{
if (param.ParameterType == typeof(string))
{
res.Add(method.Name);
break;
}
}
}
return res.ToArray();
}
public static void InvokeMethod(string className, string methodName, string fileName)
{
var t = Type.GetType(className);
using (StreamReader f = new StreamReader("params.txt"))
{
t.GetMethod(methodName).Invoke(t.GetConstructor(Type.EmptyTypes).Invoke(new object[] { }),
new object[] { f.ReadLine() });
}
}
}
class Program
{
static void Main(string[] args)
{
string name = "MyClass";
foreach (var x in MyTestClass.GetMethodsWithStrParams(name))
{
Console.WriteLine(x);
}
MyTestClass.InvokeMethod("MyClass", "Method4", "params.txt");
Console.ReadKey(true);
}
}
this is the output when i run the program
method3
method3
method4
Method returns a char, accepts string. .
but inside params.txt there is
10 1.5

Some suggestions:
Split the reflection part into two: 1) the construction of the object; 2) the invoke of the method. This makes the code a bit clearer and you could maybe re-use the object for the invoke of a next method.
The f.ReadLine() returns a single string. It does not automatically split this into separate arguments. The new object[] { f.ReadLine() } gives an object-array with a single string value.
You will need to split that line yourself on some separator into separate values. Then you will need to convert those separate strings into the parameter-types your method needs.

I run your code in visual Studio and the only problem I had was the file name. the rest seemed to work in principle. is the file params.txt definitely in executable folder? If not you will need to provide full path.
MyTestClass.InvokeMethod("MyClass", "Method4", "c:\\params.txt");
Another question I have is? does MyClass exist within a namespace? If so you will need to use namespace to return Type object:
MyTestClass.InvokeMethod("MyNamespace.MyClass", "Method4", "c:\\params.txt");
Also I just noticed that despite the fact that method InvokeMethod accepts filename as argument it has got a hard-coded filename. It therefore ignores filename passed by calling code (in main method).

Related

Passsing a value to a method using a custom attribute

I'm trying to understand how can I use custom attributes to call method passing a parameters
[ExecuteMe("hello", "reflection")]
public void M3(string s1, string s2)
{
Console.WriteLine("M3 s1={0} s2={1}", s1, s2);
}
I'm trying to call this method using this code:
static void Main(string[] args)
{
var assembly= Assembly.LoadFrom("MyLibrary.dll");
foreach (var type in assembly.GetTypes())
{
object act = Activator.CreateInstance(type);
var methodInfos = type.GetMethods().Where(m => m.GetCustomAttributes(typeof(ExecuteMe)).Any());
foreach (var mInfo in methodInfos)
{
//Console.WriteLine(mInfo.Name);
var argument = mInfo.GetParameters();
foreach (var a in argument)
{
Console.WriteLine(a);
// a.RawDefaultValue;
mInfo.Invoke(act, new object[]{a});
}
}
if (type.IsClass)
Console.WriteLine(type.FullName);
}
Console.ReadLine();
}
It doesn't work because "a" is a ParameterInfo and invoke want a Object[].
What am I doing wrong and how do I get those values?
this is my attribute:
public class ExecuteMe : Attribute
{
public object[] args;
public ExecuteMe(params object[] _args)
{
this.args = _args;
}
}`
I've rewritten it a little. You never actually accessed the arguments you gave to your attribute.
namespace StackOverflow
{
using System;
using System.Reflection;
[AttributeUsage(AttributeTargets.Method, Inherited = false, AllowMultiple = true)]
public class ExecuteMe : Attribute
{
public object[] Arguments { get; }
public ExecuteMe(params object[] args)
{
this.Arguments = args;
}
}
public class TestSubject
{
[ExecuteMe(5, "Hello")]
[ExecuteMe(7, "World")]
public int Function(int i, string s)
{
Console.WriteLine("Executing TestSubject.Function with parameters {0} and {1}", i, s);
return 42;
}
}
internal static class Program
{
internal static void Main(string[] args)
{
// This could come from another dll, for example
// var assembly = Assembly.LoadFrom("MyLibrary.dll").GetTypes();
var availableTypes = Assembly.GetExecutingAssembly().ExportedTypes;
foreach (var type in availableTypes)
{
foreach (var method in type.GetMethods())
{
foreach (var attribute in method.GetCustomAttributes<ExecuteMe>())
{
var instance = Activator.CreateInstance(type);
method.Invoke(instance, attribute.Arguments);
}
}
}
Console.ReadLine();
}
}
}
This should yield:
To make sure I understand what you're trying to do, if a method has an ExecuteMe attribute, you want to call the method, passing the arguments from the attribute to the method?
I'm going to assume that this is just for experimentation and you already realize that this doesn't guarantee whether the number or type of arguments supplied to the attribute will match the number and type of arguments the method requires. The attribute takes an unlimited number of objects, while the method requires two strings.
The issue that you're seeing is that you're looking at .GetParameters which doesn't tell you anything about the values coming from the attribute. It just describes what the parameters of the method are.
What you need is to get the args property from the attribute and pass those values when you invoke the method.
Just for the sake of illustration I'm going to use a method where the signatures match.
public class ClassWithMethod
{
[ExecuteMe("hello", "reflection")]
public void M3(params object[] args)
{
var strings = args.Where(arg => arg != null).Select(arg => arg.ToString());
Console.WriteLine(string.Join(", ", strings));
}
// Just to verify that we're only invoking methods with the attribute.
public void MethodWithoutAttribute() { }
}
...and in the console app I'm going to read types from the executing assembly just for convenience.
I rearranged a few things but you'll see what's going on:
static void Main(string[] args)
{
var assembly = Assembly.GetExecutingAssembly();
foreach (var type in assembly.GetTypes())
{
var methodInfos = type.GetMethods();
// looking at all the methods, not yet narrowing it down to those
// with the attribute.
foreach (var mInfo in methodInfos)
{
// We don't just want to know if it has the attribute.
// We need to get the attribute.
var executeMeParameter = mInfo.GetCustomAttribute<ExecuteMe>();
// If it's null the method doesn't have the attribute.
// Ignore this method.
if (executeMeParameter == null) continue;
// We don't need to create the instance until we know that we're going
// to invoke the method.
object act = Activator.CreateInstance(type);
// Pass the args property of the attribute (an array of objects)
// as the argument list for the method.
mInfo.Invoke(act, new object[]{executeMeParameter.args});
}
if (type.IsClass)
Console.WriteLine(type.FullName);
}
Console.ReadLine();
}
In this case we're just passing all of the arguments from the attribute. This is the part where it's a little messy. What if args had three string values but the method had a single int parameter?
This part is a little weird. I had to do this because of the params keyword.
mInfo.Invoke(act, new object[]{executeMeParameter.args});
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Another reason why I'm assuming that this is just for experimentation is that if you wanted to use an attribute to determine which method to run and to pass in hard-coded parameters (which itself is something I can't see doing), this would be much easier:
[ExecuteMe]
public void CallM3()
{
M3("Hello", "reflection");
}
public void M3(params object[] args)
{
var strings = args.Where(arg => arg != null).Select(arg => arg.ToString());
Console.WriteLine(string.Join(", ", strings));
}
...and the attribute has no arguments:
public class ExecuteMe : Attribute
{
}
The difference now is that everything is strongly typed and compiles. You don't have to worry about whether the parameters will match up at runtime.

cannot infer type parameters using Func [duplicate]

This question already has answers here:
Why can't C# infer type from this seemingly simple, obvious case
(5 answers)
Closed 7 years ago.
A colleague wrote this extension method that I wanted to produce an example for:
namespace ExtensionMethods {
public static class MyExtensions {
public static Res MyAggregate<T, Res>(this IEnumerable<T> source, Func<Res, int, T, Res> f) {
var i = 0;
Res result = default(Res);
foreach (T x in source) {
result = f(result, i, x);
i++;
}
return result;
}
}
}
It creates a generic Aggregate method that also includes an index.
My example (that follows) takes a list of strings and joins the 1st letter from the first word, the 2nd from the second, etc..
namespace ExtensionMethods {
public class Program {
public static string OffsetChars(string x, int i, string y) {
return x + y[i];
}
static void Main(string[] args) {
List<string> test = new List<string>() { "hello", "there", "you" };
// get h from (h)ello, h from t(h)ere and u from yo(u) (hhu)
string result = test.MyAggregate<string, string>(OffsetChars);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
My question is about this line (the important one):
string result = test.MyAggregate<string, string>(OffsetChars);
Without <string, string> there is an error that the types of arguments cannot be inferred from usage. My question(s):
Why cannot they be inferred? Is there something missing from my code that would enable them to be inferred?
I tested with an explicit delegate (follows) but the same error occurs:
namespace ExtensionMethods {
delegate string OffsetMethod(string x, int i, string y);
public class Program {
public static string OffsetChars(string x, int i, string y) {
return x + y[i];
}
static void Main(string[] args) {
List<string> test = new List<string>() { "hello", "there", "you" };
// get h from (h)ello, h from t(h)ere and u from yo(u) (hhu)
OffsetMethod myMethod = OffsetChars;
string result = test.MyAggregate(myMethod);
Console.WriteLine(result);
Console.ReadKey();
}
}
}
To summarize, I want to ensure that I haven't missed anything with my code and, assuming that I haven't, to understand why the parameter types cannot be inferred.
Your method is just a delegate and therefore does not have any generic type arguments that could be infered.
When you define OffsetChars as a generic Func, they can be infered just fine:
public static Func<string, int, string, string> OffsetChars = (x, i, y) =>
{
return x + y[i];
};
WERTZUI is right, because the delegate does not have any generic arguments, compiler cannot infer it, so you have se error.

C# Reflection Invoke - Object of Type 'XXX' Cannot Be Converted to type 'System.Object[]'

I have created an instance called input that is of type:
public class TestInput
{
public int TesTInt { get; set; }
}
I use this in this function:
public static class TestClass
{
public static string TestFunction()
{
var testInput = new TestInput();
string res = ServicesManager.Execute<string>((object) testInput);
return res;
}
}
The Execute function is here:
public static OUT Execute<OUT>(object input)
where OUT : class
{
var method = //getting method by reflection
object[] arr = new object[] { input };
return method.Invoke(null, arr) as OUT; //Error is triggered here
}
The method that I invoke is this one:
public static string TestFunctionProxy(object[] input)
{
var serviceInput = input[0] as TestInput;
//rest of code
}
I received the error in the title. (XXX - "TestInput" type)
What's happening and what is causing this error?
Note: method is static so no instance is required for the first parameter. Please correct me if I'm wrong.
Any help is appreciated.
EDIT: Updated the question with some more code for a complete example.
You are passing the wrong arguments to the method. It wants an object[] and you are giving a simpe object. This is how to fix it:
object[] arr = new object[] { new object[] { input } };
The 'outer' object[] is the parameter for Invoke, the 'inner' array is the parameter for your method.

How to pass two parameter arguments int and double so that the invoke method can work?

I have a question on reflection in c sharp. This is my question
1) define class MyClass with different fields with different accessors(private, public, protected) also methods with different argument set and return types
2) define MyTestClass which contains method, that do the following: print method names for spicified class name, where methods contain string arguments, class name is string value. Call some method of class, and put arguments to method, arguments should read from text file(name of class and name of method is arguments of method
I want to invoke method5 in my class but it takes two parameters and when I try to I'm getting a mismatch count parameter error, how can I pass two parameters int and double so that the invoke method works?
Inside params.txt there is
10 1.5
and I want to read from a text file, this below is my full code any ideas or modification I will appreciate
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.IO;
class MyClass
{
private int i;
public double d;
private string s;
public bool b;
public MyClass()
{
i = 1;
d = 0.1;
s = "1";
b = true;
}
public void Method0()
{
Console.WriteLine("Method with no arguments, no return value.");
}
private int Method1(int arg0)
{
Console.WriteLine("The method returns int, int gets.");
return arg0;
}
private double Method2(int arg0, double arg1)
{
Console.WriteLine("Method returns a double, taking int and double.");
return arg1 * arg0;
}
public bool Method3(string arg0)
{
Console.WriteLine("Method returns a bool, accepts string");
return arg0.Length>10;
}
public bool Method3(string arg0,string arg1)
{
Console.WriteLine("The method takes two arguments string.");
return arg0 == arg1;
}
public static char Method4(string arg0)
{
Console.WriteLine("Method returns a char, accepts string. .");
Console.WriteLine(arg0);
return arg0[1];
}
public void Method5(int arg0, double arg1)
{
Console.WriteLine("arg1 = {0} arg2 = {1}.",arg0,arg1);
}
}
class MyTestClass
{
public static string[] GetMethodsWithStrParams(string className)
{
var t = Type.GetType(className);
List<string> res = new List<string>();
foreach (var method in t.GetMethods())
{
foreach (var param in method.GetParameters())
{
if (param.ParameterType == typeof(string))
{
res.Add(method.Name);
break;
}
}
}
return res.ToArray();
}
public static void InvokeMethod(string className, string methodName, string fileName)
{
var t = Type.GetType(className);
using (StreamReader f = new StreamReader("params.txt"))
{
t.GetMethod(methodName).Invoke(t.GetConstructor(Type.EmptyTypes).Invoke(new object[] { }),
new object[] { f.ReadLine(), f.ReadLine()+"1" });
}
}
}
class Program
{
static void Main(string[] args)
{
string name = "MyClass";
foreach (var x in MyTestClass.GetMethodsWithStrParams(name))
{
Console.WriteLine(x);
}
MyTestClass.InvokeMethod("MyClass", "Method5", "params.txt");
Console.ReadKey(true);
}
}
You need to convert all strings from the file to their respective types used in the method:
using System;
using System.Linq;
using System.IO;
class MyInvokedClass
{
public void MyInvokedMethod(int arg0, double arg1)
{
Console.WriteLine("arg1 = {0} arg2 = {1}.", arg0, arg1);
}
}
class Program
{
public static void InvokeMethod(string className, string methodName, string fileName)
{
string[] contents = File.ReadAllLines(fileName);
var t = Type.GetType(className);
var m = t.GetMethod(methodName);
var parametertypes = m.GetParameters().Select(p => p.ParameterType);
var parameters = parametertypes.Select((type, index) => Convert.ChangeType(contents[index], type)).ToArray();
var instance = Activator.CreateInstance(t);
m.Invoke(instance, parameters);
}
static void Main()
{
InvokeMethod("MyInvokedClass", "MyInvokedMethod", "params.txt");
Console.ReadKey(true);
}
}
The file reads:
42
12.34
(note that you may have to replace the . by the decimal point used by your programs culture to work correctly)
As this is a continuation of your previous question, here are a few problems that you need to solve:
1) I don't think that you want to have params.txt contain only arguments for Method5, otherwise your app will fail when you try another method
2) you need to parse the line from file into an array of objects.
I would do something like
params.txt:
Method5: 1 1.5
Method4: some string
...
I suggest using something like tab character as separator or a pipe (|) or anything you don't expect in valid arguments, otherwise you will not be able to have strings which contain spaces.
Now your code needs to find the line for the method being called and split it into parts to get the arguments. E.g. for method5 you will now have 2 strings representing the arguments: "1" and "1.5"
Next thing you have to do is convert these strings into objects of correct type for that method call (i.e. int and double).
One approach is to encode the argument type into params.txt:
Method5L: 1:int 1.5:float
but this is error prone and it will easily go out of sync when you change your method, so better approach is to get the argument types via reflection, i.e. from MethodInfo object representing the method:
var parameters = method.GetParameters();
The last step is doing the conversion - you could use methods like int.Parse, double.Parse or even better, the Convert class, e.g.: Convert.ChangeType("1.5", typeof(float))
var args = new objects[parameters.Length];
var argStrings = {"1", "1.5"}; // obviously, you must get these from params.txt, not hardcode it
int idx = 0;
foreach (var param in parameters) {
args[idx] = Convert.ChangeType(argStrings[idx], param.ParameterType);
idx++;
}
Now you can provide the args array as parameter list to your method via Invoke.
There it is, now you just have to code it.

How to pass a function as a parameter in C#?

Is it possible to pass a function as a parameter in C#? I can do it using the Func or Action classes, but this forces me to declare the entire function signature at once. When I try to use Delegate, I get a compile error saying it can't convert a method group to a Delegate.
I'm working on Axial and I'm trying to allow users to call web services. What I'm going for is the ability to create the Visual Studio proxy class and then pass in the generated function. The function signature doesn't matter because the generated code only uses the function name. However, I'd like to pass in the function instead of the name for two reasons: the ability to use the proxy's Url property and a compiler error if the web service doesn't exist or is updated in Visual Studio.
public void AlertIt(object o) {
Axial.DOM.Window.Alert(o.ToString());
}
public void CallAddService() {
object[] param = new object[] { int.Parse(txtA.Text), int.Parse(txtB.Text) };
Axial.ServerScript.CallWebService(new WSProxy.WS().Add, param, AlertIt, AlertIt);
}
class Axial.ServerScript {
public void CallWebService(Delegate method, object[] param, Action<object> successCallback, Action<object> failureCallback) {
// translate to javascript (already working)
}
}
I think what you want is:
static object InvokeMethod(Delegate method, params object[] args){
return method.DynamicInvoke(args);
}
static int Add(int a, int b){
return a + b;
}
static void Test(){
Console.WriteLine(InvokeMethod(new Func<int, int, int>(Add), 5, 4));
}
Prints "9".
Converting a method group, anonymous method or lambda expression to a delegate requires the compiler to know the exact delegate type. However, you could potentially use lambda expressions and captured variables to make this simpler:
public void InvokeMethod(Action action)
{
action();
}
public int Add(int a, int b)
{
return a + b;
}
public void Test()
{
InvokeMethod(() => Add(2, 3));
}
That basically delays invocation in the normal way, but by wrapping the actual call to Add in a plain Action delegate.
If that doesn't fulfil your requirements, perhaps you can tell us a bit more about what you're really trying to achieve.
EDIT: If this is generated code, you can cast to a Func<...> with the right type arguments - assuming there aren't too many. Other than that, there's no real way of just passing in a method group. There's been occasional calls for an "infoof(...)" operator (like typeof but for members) which would give you a MemberInfo, but that doesn't actually exist.
You should have a delegate first
delegate int Operation(int a, int b)
then it becomes:
public void InvokeMethod(Operation method, object target, object param)
{
method((int) target, (int) param);
}
No need for any call to Invoke.
As with dbone I'm unsure why you would need a params[] array. Would you clarify the expanded usage for the params?
Also, I'll have to correct something in your question though, because it will cause a compilation error :p
please have a look at using delegates here is a great example
Delegate Example
why are you using reflection? will there ever be a different number of params? or do you know the method signture will remain constant (also remember C# supports the params[] keyword)
params c#
HTH
Bones
Look at Functional Programming Series by Justin Etheredge.
You should find solution to your problem there.
This is much simple example, to programmer who already familiar with (C/C++/VB.NET/Python)-style pass function by pointer/ref (with C# delegate):
delegate void CALLBACK(String s);
static void Main(string[] args)
{
Get("some string", testfunc);
Util.pause();
}
static void Get(String s, CALLBACK x)
{
x(s);
}
static void testfunc(String s)
{
Console.WriteLine(s);
}
Say If you need to pass the method as parameter as well as you need to catch the return value for further processing . Then the above examples will work fine .
But say if you need to pass a method with void return type then you need to create one more version of the InvokeMethod function.
Check the example below.
private static T retry<T>(Delegate method, params object[] args)
{
for (int i = 0; i <= 3; i++)
{
try
{
return (T)method.DynamicInvoke(args);
}
catch (Exception ex)
{
if (i == 3)
{
logMessage(ex.Message);
}
Console.WriteLine("Retry count " + i);
Thread.Sleep(10);
}
}
return default(T);
}
private static void retry2(Delegate method, params object[] args)
{
for (int i = 0; i <= 3; i++)
{
try
{
method.DynamicInvoke(args);
break;
}
catch (Exception ex)
{
if (i == 3)
{
logMessage(ex.Message);
//return default(T);
}
Console.WriteLine("Retry count " + i);
Thread.Sleep(10);
}
}
}
static bool isSuccess = true;
static void logMessage(string msg)
{
isSuccess = false;
Console.WriteLine(msg);
}
static int Add(int a, int b)
{
return a + b;
}
static void Add2(int a, int b)
{
int c = a + b;
Console.WriteLine(c);
}
static void Main(string[] args)
{
int d = retry<int>(new Func<int, int, int>(Add), 6, 7.7);
Console.Write(" " + d + "\n"+isSuccess);
retry2(new Action<int, int>(Add2), 45, 60);
Console.ReadKey();
}
Something like this ought to work for you:
delegate int MyDelegate(int a, int b);
public int Add(int a, int b) {
return a + b;
}
public void InvokeMethod(Delegate method, object[] param) {
Console.WriteLine(method.DynamicInvoke(param));
}
public Form1() {
InitializeComponent();
InvokeMethod(new MyDelegate(Add), new object[] { 1, 2 });
}
Good luck!

Categories