I am trying to understand Reflection in c# and I am trying to get the following code to work. The first method (GetUserName) works but the second method (AddGivenNumbers) is giving me an exception error of "parameter type mismatch".
I created a class library with the 2 methods and am trying to use reflection in main console application.
Class Library Code:
namespace ClassLibraryDELETE
{
public class Class1
{
public string GetUserName(string account)
{
return "My name is " + account;
}
public int AddGivenNumbers(int num1, int num2)
{
return num1 + num2;
}
}
}
Code in my main console application:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Reflection;
namespace ConsoleApplicationDELETE_REFLECTION
{
class Program
{
static void Main(string[] args)
{
Assembly assemblyInstance = Assembly.LoadFrom(#"C:\Users\xyz\Desktop\Debug\ClassLibraryDELETE.dll");
Type Class1Type = assemblyInstance.GetType("ClassLibraryDELETE.Class1");
object class1Instance = Activator.CreateInstance(Class1Type);
MethodInfo getMethodFullName = Class1Type.GetMethod("GetUserName");
string[] parameter = new string[1];
parameter[0] = "John Doe";
string userName = (string)getMethodFullName.Invoke(class1Instance, parameter);
Console.WriteLine("User Name = {0}", userName);
Assembly assemblyInstance2 = Assembly.LoadFrom(#"C:\Users\xyz\Desktop\Debug\ClassLibraryDELETE.dll");
Type ClassType2 = assemblyInstance.GetType("ClassLibraryDELETE.Class1");
object class1Instance2 = Activator.CreateInstance(ClassType2);
MethodInfo getMethodFullName2 = ClassType2.GetMethod("AddGivenNumbers");
//object[] parameters = new object[2];
//parameters[0] = 8;
//parameters[1] = 4;
object[] args2 = new object[] { 1, 2 };
object result = getMethodFullName.Invoke(class1Instance2, args2);
Console.WriteLine("Sum of the two numbers is {0}", result);
Console.ReadLine();
}
}
}
You have a typo
object result = getMethodFullName.Invoke(class1Instance2, args2);
You should have been targeting getMethodFullName2, otherwise you're trying to execute the first function (which takes 1 argument) with 2 arguments.
Working example: http://rextester.com/WBFYB30834
Related
Using the Roslyn API with Visual Studio 2015, can I convert an object instance to source code? Can I create an extension method like ".ToSourceCode()" as shown below?
class Foo { }
class Program
{
static string classSourceCode = "class Foo { }";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
No. However, ILSpy can.
Based on the comments on the question and what I understand about Roslyn, decompilation is not supported. However, thanks to #Bradley's ILSpy tip, there is a solution:
Download the ILSpy binaries from http://ilspy.net/
Reference the following assemblies: ICSharpCode.Decompiler.dll, ILSpy.exe, Mono.Cecil.dll, ILSpy.BamlDecompiler.Plugin.dll
Implement the ".ToSourceCode()" extension method as shown below:
using System;
using System.Linq;
using System.Reflection;
using System.Text.RegularExpressions;
using ICSharpCode.Decompiler;
using ICSharpCode.ILSpy;
using Mono.Cecil;
class Foo { }
class Program
{
static string classSourceCode = "using System; internal class Foo { } ";
static void Main()
{
var instance = new Foo();
var instanceSourceCode = instance.GetType().ToSourceCode();
System.Diagnostics.Debug.Assert(instanceSourceCode == classSourceCode);
}
}
static class TypeExtensions
{
public static string ToSourceCode(this Type source)
{
var assembly = AssemblyDefinition.ReadAssembly(Assembly.GetExecutingAssembly().Location);
var type = assembly.MainModule.Types.FirstOrDefault(t => t.FullName == source.FullName);
if (type == null) return string.Empty;
var plainTextOutput = new PlainTextOutput();
var decompiler = new CSharpLanguage();
decompiler.DecompileType(type, plainTextOutput, new DecompilationOptions());
return Regex.Replace(Regex.Replace(plainTextOutput.ToString(), #"\n|\r", " "), #"\s+", " ");
}
}
My code:
//App, Core.cs
using System;
using System.IO;
using System.Reflection;
namespace Game
{
public static void Main(string[] args)
{
Assembly a = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mods\\ExampleMod.dll"));
var x1 = a.GetType("PTF_Mod.Mod_Main");
var x2 = x1.GetMethod("OnStart");
var x3 = x2.Invoke(null, new object[] { });
while(true);
}
}
//App, ModCrew.cs
using System;
using System.Reflection;
namespace Engine
{
public static class ModCrew
{
public class Mod
{
public void ItWorks()
{
Console.WriteLine("It works!");
}
}
}
}
//DLL, Mod_Main.cs
using System;
using System.Reflection;
namespace PTF_Mod
{
public static class Mod_Main
{
public static void OnStart()
{
var exe = Assembly.GetCallingAssembly();
Console.WriteLine(exe.Location); //Location is valid
var x = exe.GetType("Engine.ModCrew.Mod", true); //But here I get exception
var y = Activator.CreateInstance(x);
x.GetMethod("ItWorks", BindingFlags.Instance).Invoke(null, object[] { });
}
}
}
Exception:
An exception of type 'System.TypeLoadException' occurred in mscorlib.dll but was not handled in user code
Additional information: Nie można załadować typu 'Engine.ModCrew.Mod' z zestawu 'Game, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null'.
You should always use BindingFlags when getting methods via reflection.
Invoking an instance with MethodInfo.Invoke requires the instance as the first parameter MethodInfo.Invoke(MyInstance,...)
Changes based on comments:
public static void Main(string[] args)
{
Assembly a = Assembly.LoadFile(Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "mods\\ExampleMod.dll"));
var x1 = a.GetType("PTF_Mod.Mod_Main");
var x2 = x1.GetMethod("OnStart", BindingFlags.Static | BindingFlags.Public);
var x3 = x2.Invoke(null, null);
while(true);
}
Mod_Main:
public static void OnStart()
{
var exe = Assembly.GetCallingAssembly();
Console.WriteLine(exe.Location); //Location is valid
var x = exe.GetType("Engine.ModCrew+Mod", true); //But here I get exception
var y = Activator.CreateInstance(x);
x.GetMethod("ItWorks", BindingFlags.Instance | BindingFlags.Public).Invoke(y, null);
}
Also, consider if reflection is even necessary, it can make programs overly complicated. If it's necessary you should look into Dynamic to avoid a lot of the troubles of invoking methods with reflection
i try to use CompileAssemblyFromSource to change 1 value at my main class.
But when i compile i get error "Could not load file or assembly or one of its dependencies" and this only happens when i try change static value of other class. But if i return some output or wrote anything at Console from this FooClass than all work's fine. But how can i change value of other class?
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
You can't find the type because you have compilation error in your code.You can't access the classes in your current code in this manner. You should at least reference the current assembly in your in-memory assembly.
UPDATE
You have two issues in your code. First, you have to make the class Program public. Then you should specify the full name of type in GetType method.
This code works fine:
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace stringToCode
{
public class Program
{
public static int q = 0;
static void Main(string[] args)
{
string source = "namespace stringToCode { public class FooClass { public void Execute() { Program.q = 1; } } }";
Console.WriteLine("q=" + q);
using (var foo = new CSharpCodeProvider())
{
var parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
{
try
{
string location = assembly.Location;
if (!String.IsNullOrEmpty(location))
{
parameters.ReferencedAssemblies.Add(location);
}
}
catch (NotSupportedException)
{}
}
var res = foo.CompileAssemblyFromSource(parameters ,source);
var type = res.CompiledAssembly.GetType("stringToCode.FooClass"); //<- here i has error
var obj = Activator.CreateInstance(type);
var output = type.GetMethod("Execute").Invoke(obj, new object[] { });
Console.WriteLine("q=" + q);
Console.ReadLine();
}
}
}
}
Here's my code:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.CSharp;
using System.CodeDom.Compiler;
using System.Reflection;
namespace ConsoleApplication1
{
class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("ConsoleApplication1.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
method.Invoke(null, null);
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace ConsoleApplication1
{
class Program
{
public static void DynamicMethod()
{
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}
I would like to know if It's possible to access variable int xx from the runetime code (eg. putting xx = 2; line after "hello world". That would be awesome
Thanks :)
Yes, you can make that available, but you need to:
add a reference to the assembly that contains xx (I'm calling this assembly StaticAssembly, for convenience - so it is probably StaticAssembly.exe, the console exe that is running)
make Program in StaticAssembly a public type so that it is accessible
either make xx into a static field, or instantiate an instance of Program and pass it to the dynamic code somehow
The following works, for example (I'm using the "instantiate an instance and pass it" approach, so I've added a parameter to DynamicMethod):
using System;
using System.CodeDom.Compiler;
using System.Reflection;
using Microsoft.CSharp;
namespace StaticAssembly
{
public class Program
{
public int xx = 1; // variable I want to access from the runtime code
static void Main(string[] args)
{
CSharpCodeProvider provider = new CSharpCodeProvider();
CompilerParameters parameters = new CompilerParameters();
parameters.GenerateInMemory = true;
parameters.ReferencedAssemblies.Add("System.dll");
parameters.ReferencedAssemblies.Add("StaticAssembly.exe");
CompilerResults results = provider.CompileAssemblyFromSource(parameters, GetCode());
var cls = results.CompiledAssembly.GetType("GeneratedAssembly.Program");
var method = cls.GetMethod("DynamicMethod", BindingFlags.Static | BindingFlags.Public);
var p = new Program();
p.xx = 42;
method.Invoke(null, new object[] {p});
int a = int.Parse(Console.ReadLine()); // pause console
}
static string[] GetCode()
{
return new string[]
{
#"using System;
namespace GeneratedAssembly
{
class Program
{
public static void DynamicMethod(StaticAssembly.Program p)
{
Console.WriteLine(p.xx);
Console.WriteLine(""Hello, world!"");
}
}
}"
};
}
}
}
This question already has answers here:
Select Right Generic Method with Reflection
(13 answers)
Closed 9 years ago.
is it possible to call with reflection a method with "explict type argument" <S> definition
e.g. oObject.Cast<S>() ?
where is:
IList <P> oObject = new List <P>();
I tried with
oObject.getType().InvokeMember( "Cast", BindingFlags.InvokeMethod, null, oObject, null)
but it does not work, does anyone know why?
Here is the complete test code but still it does not work. The last line produce always exception. Is it possible to make it work ?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
namespace reflection_tester
{
class CBase
{
public string Ja = "I am the base";
}
class MyClass01 : CBase
{
public string _ID;
public string ID
{
get { return _ID; }
set { _ID = value; }
}
}
class Program
{
public static object wrapper()
{
//return a list of classes MyClass01
IList<MyClass01> lstClass01 = new List<MyClass01>();
MyClass01 oClass01a = new MyClass01();
oClass01a.ID = "1";
MyClass01 oClass01b = new MyClass01();
oClass01b.ID = "2";
lstClass01.Add(oClass01a);
lstClass01.Add(oClass01b);
return lstClass01;
}
static void Main(string[] args)
{
MyClass01 oMy1 = new MyClass01();
oMy1._ID = "1";
MyClass01 oMy2 = new MyClass01();
oMy2._ID = "3";
IList<MyClass01> oListType01 = new List<MyClass01>();
oListType01.Add(oMy1);
oListType01.Add(oMy2);
object oObjectType = new object();
oObjectType = oListType01;
/* this works */
IEnumerable<CBase> enumList = oListType01.Cast<CBase>();
MethodInfo mInfo = typeof(System.Linq.Enumerable).GetMethod("Cast", new[] { typeof(System.Collections.IEnumerable) }).MakeGenericMethod(typeof(CBase));
/* this does not work, why ? throws exception */
IEnumerable<CBase> enumThroughObject = (IEnumerable<CBase>)mInfo.Invoke(oObjectType, null);
return;
}
}
}
The Cast extension method lives on the class Enumerable, and you need to call MakeGenericMethod:
typeof(System.Linq.Enumerable)
.GetMethod("Cast", new []{typeof(System.Collections.IEnumerable)})
.MakeGenericMethod(typeof(S))
.Invoke(null, new object[] { oObjectType })
update: Because the method is static, the first parameter to Invoke should be null
I think you're looking for Type.MakeGenericType