I have the following code, using Mono.Cecil:
{
ModuleDefinition module = ModuleDefinition.ReadModule("library.dll");
TypeDefinition type1 = module.Types.Where(t => "Namespace.Destination".Equals(t.FullName)).Single();
TypeDefinition type2 = module.Types.Where(t => "Namespace.Target".Equals(t.FullName)).Single();
MethodDefinition method1 = type1.Methods.Where(m => "Test".Equals(m.Name)).Single();
MethodDefinition method2 = type2.Methods.Where(m => "Test".Equals(m.Name)).Single();
var processor = methodTesta1.Body.GetILProcessor();
var newInstruction = processor.Create(OpCodes.Call, methodTesta2);
var firstInstruction = methodTesta1.Body.Instructions[0];
processor.Replace(firstInstruction, newInstruction);
}
namespace Namespace
{
public class Destination
{
public String Test()
{
Console.Write("Destination method");
}
}
public class Target
{
public String Test()
{
Console.Write("Target Method");
}
}
}
I'd not like to create a new "dll" file or overwrite the current, I want to modify class only at runtime.
How can I "persist" the modification and create a new instance of Destination class with modified method?
Is there a way to do it?
EDIT: The objective is execute a different method body, when certain method is called, wich return a certain type.
Related
I'm try to write an source generator and need the types that involved in a extension method call.
The problem is, this extension method is generated by source generator itself. So if I try get ISymbol, I get null in my Generator class.
Is it possible to get the information I need otherwise?
Here is the example
var result = someObject.ConvertTo<OtherType>();
The ConvertTo<T>() extension method is generated from source generator. I can find the correct InvocationExpressionSyntax, but how can I get the fully qualified type of the someObject and OtherType?
Here is the generator
[Generator]
public class ConvertGenerator : ISourceGenerator
{
private const string defaultNamespace = "AutoGenerators";
private const string extensionsClassName = "ConvertExtensions";
private static readonly string _classText = #$"
namespace {defaultNamespace}
{{
public static class {extensionsClassName}
{{
public static TDestination ConvertTo<TDestination>(this object source)
{{
/* generated */
return default;
}}
}} }}";
public void Initialize(GeneratorInitializationContext context)
{
#if DEBUG
if (!Debugger.IsAttached)
{
Debugger.Launch();
}
#endif
// Register a syntax receiver that will be created for each generation pass
context.RegisterForSyntaxNotifications(() => new SyntaxReceiver());
}
public void Execute(GeneratorExecutionContext context)
{
// retrieve the populated receiver
if (!(context.SyntaxReceiver is SyntaxReceiver receiver))
return;
// for testing
var invocationSyntax = receiver.Methods.FirstOrDefault();
if (invocationSyntax != null)
{
var semanticModel = context.Compilation.GetSemanticModel(invocationSyntax.SyntaxTree);
// symbol is null here
var symbol = semanticModel.GetSymbolInfo(invocationSyntax.Expression).Symbol;
// TODO: how to get type description for sourceObjectName and destinationTypeName
var convertInvocationString = invocationSyntax.ToString();
var sourceObjectName = convertInvocationString.Substring(0, convertInvocationString.IndexOf('.'));
var destTypeSubs = convertInvocationString.Substring(convertInvocationString.IndexOf('<') + 1);
var destinationTypeName = destTypeSubs.Substring(0, destTypeSubs.IndexOf('(') - 1);
}
var classSource = _classText;
context.AddSource($"{extensionsClassName}.cs", SourceText.From(classSource, Encoding.UTF8));
}
/// <summary>
/// Created on demand before each generation pass
/// </summary>
class SyntaxReceiver : ISyntaxReceiver
{
public List<InvocationExpressionSyntax> Methods { get; } = new List<InvocationExpressionSyntax>();
/// <summary>
/// Called for every syntax node in the compilation, we can inspect the nodes and save any information useful for generation
/// </summary>
public void OnVisitSyntaxNode(SyntaxNode context)
{
// any field with at least one attribute is a candidate for property generation
if (context is InvocationExpressionSyntax invocationExpressionSyntax
&& invocationExpressionSyntax.ToString().Contains("ConvertTo<"))
{
Methods.Add(invocationExpressionSyntax);
}
}
}
}
Update: Also I think I need more then just the type. I need ISymbol to get all the properties of the types
Update 2: I did a small step by making the ConvertTo<T> method partial and reference the separat project with this method. I'm getting the IMethodSymbol now and have the ITypeSymbol for OtherType, but the ITypeSymbol for someObject is the object type, because of the extension method signature. But I need the concrete type symbol for someObject
I found the solution.
First of all the ConvertTo<T> method should be declared in my project as partial, so I can get ISymbol for the invocation. It gives me the ReturnType
var semanticModel = context.Compilation.GetSemanticModel(invocationSyntax.SyntaxTree);
var mapToSymbol = semanticModel.GetSymbolInfo(invocationSyntax.Expression).Symbol as IMethodSymbol;
var convertToType = mapToSymbol.ReturnType;
Then I can use the invocationSyntax.Expression to get the type of the someObject or the parameter of the extension method
var convertFromType = TryGetSourceType(invocationSyntax.Expression, semanticModel);
...
private static ITypeSymbol TryGetSourceType(ExpressionSyntax invocationExpression, SemanticModel semanticModel)
{
switch (invocationExpression)
{
case MemberAccessExpressionSyntax memberAccessExpressionSyntax:
var symbol = semanticModel.GetSymbolInfo(memberAccessExpressionSyntax.Expression).Symbol;
return symbol switch
{
ILocalSymbol local => local.Type,
IParameterSymbol param => param.Type,
IFieldSymbol field => field.Type,
IPropertySymbol prop => prop.Type,
IMethodSymbol method => method.MethodKind == MethodKind.Constructor ? method.ReceiverType : method.ReturnType,
_ => null
};
default:
return null;
}
}
I am using Mono.Cecil to edit my target method's IL code so that I can log that method's entry point, without editing the actual code.
I am able to insert a call instruction to a method which can perform logging operation.
But I don't know how to log my target method's input parameters.
In short i want to insert an instruction in the target method by changing it's IL code to do a log or say print operation to log the input parameter values passed to that method.
I tried a basic program as sample.
public class Target
{
// My target method.
public void Run(int arg0, string arg1)
{
Console.WriteLine("Run method body");
}
}
public static class Trace{
// This is my log method, which i want to call in begining of Run() method.
public void LogEntry(string methodName, object[] params)
{
System.Console.WriteLine("******Entered in "+ methodName+" method.***********")
// With params :......
//
}
}
Source program.
public class Sample
{
private readonly string _targetFileName;
private readonly ModuleDefinition _module;
public ModuleDefinition TargetModule { get { return _module; } }
public Sample(string targetFileName)
{
_targetFileName = targetFileName;
// Read the module with default parameters
_module = ModuleDefinition.ReadModule(_targetFileName);
}
public void Run(string type, string method)
{
// Retrive the target class.
var targetType = _module.Types.Single(t => t.Name == type);
// Retrieve the target method.
var runMethod = targetType.Methods.Single(m => m.Name == method);
// Get a ILProcessor for the Run method
var processor = runMethod.Body.GetILProcessor();
// get log entry method ref to create instruction
var logEntryMethodReference = targetType.Methods.Single(m => m.Name == "LogEntry");
// Import ..
//
var newInstruction = processor.Create(OpCodes.Call, logEntryMethodReference);
var firstInstruction = runMethod.Body.Instructions[0];
processor.InsertBefore(firstInstruction, newInstruction);
// Write the module with default parameters
_module.Write(_targetFileName);
}
}
Well, this was interesting :)
Here's my working sample (comments in the code, feel free to ask anything, if not clear):
Modified sample (to actually write out the parameters):
public class Target
{
// My target method.
public void Run(int arg0, string arg1)
{
Console.WriteLine("Run method body");
}
}
public static class Trace
{
// This is my log method, which i want to call in begining of Run() method.
public static void LogEntry(string methodName, object[] parameters)
{
Console.WriteLine("******Entered in " + methodName + " method.***********");
Console.WriteLine(parameters[0]);
Console.WriteLine(parameters[1]);
}
}
Source program to handle IL injection:
public class Sample
{
private readonly string _targetFileName;
private readonly ModuleDefinition _module;
public ModuleDefinition TargetModule { get { return _module; } }
public Sample(string targetFileName)
{
_targetFileName = targetFileName;
// Read the module with default parameters
_module = ModuleDefinition.ReadModule(_targetFileName);
}
public void Run(string type, string method)
{
// Retrive the target class.
var targetType = _module.Types.Single(t => t.Name == type);
// Retrieve the target method.
var runMethod = targetType.Methods.Single(m => m.Name == method);
// Get a ILProcessor for the Run method
// get log entry method ref to create instruction
var logEntryMethodReference = _module.Types.Single(t => t.Name == "Trace").Methods.Single(m => m.Name == "LogEntry");
List<Instruction> newInstructions = new List<Instruction>();
var arrayDef = new VariableDefinition(new ArrayType(_module.TypeSystem.Object)); // create variable to hold the array to be passed to the LogEntry() method
runMethod.Body.Variables.Add(arrayDef); // add variable to the method
var processor = runMethod.Body.GetILProcessor();
newInstructions.Add(processor.Create(OpCodes.Ldc_I4, runMethod.Parameters.Count)); // load to the stack the number of parameters
newInstructions.Add(processor.Create(OpCodes.Newarr, _module.TypeSystem.Object)); // create a new object[] with the number loaded to the stack
newInstructions.Add(processor.Create(OpCodes.Stloc, arrayDef)); // store the array in the local variable
// loop through the parameters of the method to run
for (int i = 0; i < runMethod.Parameters.Count; i++)
{
newInstructions.Add(processor.Create(OpCodes.Ldloc, arrayDef)); // load the array from the local variable
newInstructions.Add(processor.Create(OpCodes.Ldc_I4, i)); // load the index
newInstructions.Add(processor.Create(OpCodes.Ldarg, i+1)); // load the argument of the original method (note that parameter 0 is 'this', that's omitted)
if (runMethod.Parameters[i].ParameterType.IsValueType)
{
newInstructions.Add(processor.Create(OpCodes.Box, runMethod.Parameters[i].ParameterType)); // boxing is needed for value types
}
else
{
newInstructions.Add(processor.Create(OpCodes.Castclass, _module.TypeSystem.Object)); // casting for reference types
}
newInstructions.Add(processor.Create(OpCodes.Stelem_Ref)); // store in the array
}
newInstructions.Add(processor.Create(OpCodes.Ldstr, method)); // load the method name to the stack
newInstructions.Add(processor.Create(OpCodes.Ldloc, arrayDef)); // load the array to the stack
newInstructions.Add(processor.Create(OpCodes.Call, logEntryMethodReference)); // call the LogEntry() method
foreach (var newInstruction in newInstructions.Reverse<Instruction>()) // add the new instructions in referse order
{
var firstInstruction = runMethod.Body.Instructions[0];
processor.InsertBefore(firstInstruction, newInstruction);
}
// Write the module with default parameters
_module.Write(_targetFileName);
}
}
I am trying to use GetType method for my custom class . I have the name of the class as string and i want to get type of it dynamically. I have the same name for two different classes which are located in different directories.
For Example:
MyClass.cs in Folder1:
namespace ConsoleApplication1.Folder1
{
public class MyClass : IClass
{
public void PrintMe()
{
System.Console.WriteLine("I am Folder 1 Class");
}
}
}
MyClass.cs in Folder2:
namespace ConsoleApplication1.Folder2
{
public class MyClass : IClass
{
public void PrintMe()
{
System.Console.WriteLine("I am Folder 2 Class");
}
}
}
Namespace is ConsoleApplication1
different classes with the same name are in the Folder1 and Folder2.
I want to get it's type from such a string:
var runtimeString = "Folder1.MyClass"
There is method mentioned in MSDN named GetType(string fileName)
How can i get type of the file and resolve it from the serviceLocator with type on runtime like:
var typeOfMyClass = GetType(runtimeString);
var instanceOfMyClass = ServiceLocator.Resolve<TypeOfMyClass>();
You appear to be describing a need for a factory method, something along the lines of:
public class MyClassFactory : IMyClassFactory
{
private Dictionary<string, Action<IClass>> _factory =
new Dictionary<string, Action<IClass>>
{
["Folder1.MyClass"] = () => new ConsoleApplication1.Folder1.MyClass(),
["Folder2.MyClass"] = () => new ConsoleApplication1.Folder2.MyClass(),
...
};
public IClass GetClassInstance(string myClassName)
{
if (_factory.Contains(myClassName))
{
return _factory[myClassName]();
}
throw NoSuchClassException(myClassName);
}
}
I believe the following is what you are trying to accomplish:
static void Main(string[] args)
{
var runtimeString = "Folder1.MyClass";
IClass instanceOfMyClass = (IClass)CreateInstance(runtimeString);
instanceOfMyClass.PrintMe();
Console.ReadKey();
}
private static object CreateInstance(string className)
{
var type = Assembly.GetExecutingAssembly().GetTypes()
.First(t => t.FullName.EndsWith(className));
return Activator.CreateInstance(type);
}
You may use Activator.CreateInstance() method to create an object of a class from its name string as below.
Create the Type object:
Type type1 = typeof(MyClass);
or
Type type1 = Type.GetType("MyClass");
Create an instance of that type:
Object o = Activator.CreateInstance(type1);
I have a class Class A, and a class Class B.
class B is a child of class A so that:
public class Class A
{
public DateTime FileStart
{
get
{
return Header.StartTime;
}
set{ }
}
...
...
}
and
public class B : A
{
FileInfo zippedA;
public A myA = null;
internal B(FileInfo mFileInfo)
{
...
//collects the same data as A from the fileinfo such as start time...
...
}
public A getAData()
{
UnZipFile(zippedA);
return myA;
}
...
}
So I'm looking for a way to call getAData() whenever an object of B is called as A for example the list Xlist stores all As and Bs but will be accessed from several places in the code:
SortedList Xlist = new SortedList();
public void GetFrames(DateTime desiredStartTime, DateTime desiredEndTime)
{
for(int fileIdx = Xlist.Values.Count-1; fileIdx >= 0; --fileIdx)
{
//my hope is that there is a way to set up B in it's class to say
// "if I get called as an A, I'll perform getAData() and return myA instead.
A rec = (A)Xlist.GetByIndex(fileIdx);
...
...
}
}
in the above instance I would like for every time an object is pulled from Xlist if its a B but gets caste as an A like so, it automatically calls the getAData() function and returns the resulting A instead of its self. is this possible??
You can make the method in parent class virtual and override it in the child classes. In doing so anywhere you call the method on an instance of type A, it will invoke the method in the derived type if the derived type provides and override, otherwise it will invoke the version in type A.
This is the simplest way, the alternative aren't very attractive. For more information on virtual methods in C# check out this msdn article; http://msdn.microsoft.com/en-us/library/aa645767(v=vs.71).aspx
To do what you think you want to do (I'm pretty sure it's not actually what you want to do) you can do this;
for(int fileIdx = Xlist.Values.Count-1; fileIdx >= 0; --fileIdx)
{
A rec = (A)Xlist.GetByIndex(fileIdx);
if (rec.GetType() == typeof(B))
{
B temp = (B) rec;
rec = temp.getAData();
}
}
Although, again, this makes no sense at all. Here's an example;
public class Car
{
int year;
bool manual;
}
public class Porsche : Car
{
bool specialPorscheOnlyFeature;
Engine enginge;
}
public class Engine
{
string engineType;
}
// in some method
Porsche p = new Porsche();
// to get Car data
int yearOfCar = p.year;
bool isManual = p.manual;
bool specialFeature = p.SpecialPorscheOnlyFeature;
Above is an example of how inheritance works. I don't retrieve an instance of the base class, everything the base class has is baked into the instance of the derived class. You're acting like the base class is some other object the derived class is composed of.
It may not be the best way to do it, but would this not work?
class File
{
public string FileInfo = "";
public override string ToString()
{
return FileInfo;
}
public virtual File GetRaw()
{
return this;
}
}
class ZippedFile : File
{
public File Unzip()
{
// Do actual unzip here..
return new File { FileInfo = FileInfo.Substring(0,8) };
}
public override File GetRaw()
{
return Unzip();
}
}
class Program
{
static void Main(string[] args)
{
List<object> files = new List<object>();
files.Add(new File { FileInfo = "BeepBoop" });
files.Add(new ZippedFile { FileInfo = "BeepBoopfQAWEFRLQER:LKAR:LWEasdfw;lekfrqW:ELR" });
files.Add(new File { FileInfo = "BoopBeep" });
files.Add(new ZippedFile { FileInfo = "BoopBeepAWSLF:KQWE:LRKsdf;lKWEFL:KQwefkla;sdfkqwe" });
foreach(var f in files)
{
File rawFile = ((File)f).GetRaw();
Console.WriteLine(rawFile);
}
Console.ReadKey();
}
}
class Parent{
public string Name{ get; set; }
}
class Child :Parent{
public string address{ get; set; }
}
[TestClass]
class TestClass{
[TestMethod]
public void TestMethod()
{
var c = new Fakes.Child();
c.addressGet = "foo"; // I can see that
c.NameGet = "bar"; // This DOES NOT exists
}
}
How can I set the "name" in the above code sample?
The generated class for Parent will have a constructor that looks like: ShimParent(Parent p).
All you need to do is:
var child = new ShimChild();
var parent = new ShimParent(child);
And set the appropriate values on the respective Shim's.
You'll have to declare it on the base class. The easiest way is to call the base class its AllInstances property:
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestMethod1()
{
ClassLibrary1.Child myChild = new ClassLibrary1.Child();
using (ShimsContext.Create())
{
ClassLibrary1.Fakes.ShimChild.AllInstances.addressGet = (instance) => "foo";
ClassLibrary1.Fakes.ShimParent.AllInstances.NameGet = (instance) => "bar";
Assert.AreEqual("foo", myChild.address);
Assert.AreEqual("bar", myChild.Name);
}
}
}
Also always try to add the ShimsContext to ensure the proper cleaning of your shim. Otherwise your other unit tests will also get the values returned that you have declared before.
Information on ShimsContext can be found here: http://msdn.microsoft.com/en-us/library/hh549176.aspx#ShimsContext
I've put together a solution based on previous answers, the Microsoft documentation, and my own experimentation. I've also changed the TestMethod a bit to show how I would actually use it for testing. Note: I haven't compiled this specific code, so I apologize if it doesn't work as is.
[TestClass]
class TestClass
{
[TestMethod]
public void TestMethod()
{
using (ShimsContext.Create())
{
Child child = CreateShimChild("foo", "bar");
Assert.AreEqual("foo", child.address); // Should be true
Assert.AreEqual("bar", child.Name); // Should be true
}
}
private ShimChild CreateShimChild(string foo, string bar)
{
// Create ShimChild and make the property "address" return foo
ShimChild child = new ShimChild() { addressGet = () => foo };
// Here's the trick: Create a ShimParent (giving it the child)
// and make the property "Name" return bar;
new ShimParent(child) { NameGet = () => bar };
return child;
}
}
I have no idea how the returned child knows that its Name should return "bar", but it does! As you can see, you don't even need to save the ShimParent anywhere; it's only created in order to specify the value for the Name property.
None of the suggested approaches so far would work in my opinion. After a lot of trial and error I have come up with this below code which worked for me. Basically you will have to define a delegate that initializes your child class and within that delegate you hookup a Shim of parent that your child class should inherit from.
public void TestMethod()
{
//var c = new Fakes.Child();
//c.addressGet = "foo"; // I can see that
//c.NameGet = "bar"; // This DOES NOT exists
using (ShimsContext.Create())
{
ShimChild childShim = null;
ShimChild.Constructor = (#this) =>
{
childShim = new ShimChild(#this);
// the below code now defines a ShimParent object which will be used by the ShimChild object I am creating here
new ShimParent()
{
NameSetString = (value) =>
{
//do stuff here
},
NameGet = () =>
{
return "name";
}
};
};
}
}