Is there a way in .NET to know what parameters and their values were passed to a method. Reflection way? This will be used from inside the method. It has to be generic so it can be used from any method. This is for logging purposes.
Call MethodBase.GetCurrentMethod().GetParameters().
However, it is not possible to get the parameter values; due to JIT optimization, they might not even exist anymore.
MethodInfo.GetCurrentMethod() will give you information about the current method and then get information about the parameters using GetParameters().
What you're trying to do can be achieved easily using aspect oriented programming. There are good tutorials online, I'll point to two of them:
http://ayende.com/Blog/archive/2008/07/31/Logging--the-AOP-way.aspx
http://www.codeproject.com/KB/cs/UsingAOPInCSharp.aspx
public void FunctionWithParameters(string message, string operationName = null, string subscriptionId = null)
{
var parameters = System.Reflection.MethodBase.GetCurrentMethod().GetParameters();
this.PrintParams(parameters, message, operationName, subscriptionId);
}
public void PrintParams(ParameterInfo[] paramNames, params object[] args)
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"{paramNames[i].Name} : {args[i]}");
}
}
You need AOP to achieve what you are looking for.
in c# you can use DispatchProxy to do that.
Check the following How to wrap existing object instance into DispatchProxy?
Nowadays a feature that could be used to achieve this is Roslyn's Source Generators.
In this way, the code that gets the parameters' values would be generated at compile-time based on the method definition. Could be interpreted as “compile-time reflection”.
Let's show an example to try to explain it better:
public void MethodInspectingItsOwnParameters(
[Description("First parameter")]
string paramName_1,
[Description("Second parameter")]
int paramName_2,
// ...
[Description("N-th parameter")]
object paramName_N,
// ...
[Description("Last parameter")]
bool paramName_M)
{
var paramsAndValues = new List<KeyValuePair<string, object>>();
// -
// => Put here the code that, using Roslyn's compile time
// metaprogramming, inspect the parameters from the method
// definition and at compile time generate the code that
// at run time will get the parameters's values and load
// them into [paramsAndValues]
// -
// #Rosalyn generated code
// · Code autogenerated at compile time
// => loads parameter's values into [paramsAndValues]
// -
// Eg (Hypothetical example of the code generated at compile
// time by Roslyn's metaprogramming):
//
// paramsAndValues.Add("paramName_0", paramName_1);
// ...
// paramsAndValues.Add("paramName_N", paramName_N);
// ...
// paramsAndValues.Add("paramName_M", paramName_M);
//
// - Note: this code will be regenerated with each compilation,
// so there no make sense to do nameof(paramName_N)
// to obtaint parameter's name
// #End Rosalyn generated code
foreach (var param in paramsAndValues)
{
string paramName = param.Key;
object paramValue = param.Value;
// In this section of the normal code (not generated at
// compile time) do what you require with the
// parameters/values already loaded into [paramsAndValues]
// by the compile time generated code
}
}
Related
I want to create a Lazy<> with runtime information about the contained type, but I'm not sure how to create the required Func<> initializer. I feel the answer is somewhere in Delegate.CreateDelegate, but I couldn't see how to make that work. The below illustrates what I want to do:
class ShapeTools {
abstract class Shape {}
class Square : Shape {}
Lazy<Square> aLazyShape;
ShapeTools() {
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented)
Shape GetShape(Type shapeType) { return null; }
void setup (FieldInfo field) { // aLazyShape
var funcType = typeof(Func<>).MakeGenericType(field.FieldType); // = typeof(Func<Square>)
var shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// var myFunc = Activator.CreateInstance(funcType, () => { return GetShape(shapeType); }) // doesn't compile - type doesn't match
var lazy = Activator.CreateInstance(field.FieldType, myFunc); // This takes a Func<Square>
field.SetValue(this, lazy);
}
}
You can achieve what you're looking for by using System.Linq.Expressions. Why you want to do it this way is another thing. =)
See this other answer for where the key parts about using Expression came from.
Note that your code for creating var funcType was not returning typeof(Func<Square>), but instead typeof(Func<Lazy<Square>>); I fixed that. Most things were made public for convenience when compiling. You could change the access to GetShape if you want to update BindingFlags in the call to GetMethod, for example.
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
namespace SomeNamespace
{
class Program
{
static void Main(string[] args)
{
ShapeTools st = new ShapeTools();
ShapeTools.Square s = st.aLazyShape.Value;
// s will be whatever ShapeTools.GetShape returns
}
public class ShapeTools
{
public abstract class Shape { }
public class Square : Shape { }
public Lazy<Square> aLazyShape;
public ShapeTools()
{
setup(GetType().GetFields().Where(f => f.Name == "aLazyShape").First());
}
// returns a shape matching the provided type (unimplemented, just an example)
public static object GetShape(Type shapeType) { return new Square(); }
void setup(FieldInfo field)
{ // only handles 'aLazyShape' right now
Type funcType = typeof(Func<>).MakeGenericType(field.FieldType.GenericTypeArguments[0]); // = typeof(Func<Square>)
Type shapeType = funcType.GetGenericArguments().First(); // = typeof(Square)
// get MethodInfo for the static method in this class that returns the right shape
MethodInfo getInstanceOfType = GetType().GetMethod(nameof(GetShape));
// build the Func dynamically
var typeConst = Expression.Constant(shapeType); // get the shapeType as an Expression
var callGetInstance = Expression.Call(getInstanceOfType, typeConst); // invoke our (static) method to get the instance of shape
var cast = Expression.Convert(callGetInstance, shapeType); // cast the return of our method (which is object) to the right type
var toLambda = Expression.Lambda(cast); // wrap everything in a Lambda to return our instance
var finalFunc = toLambda.Compile(); // compile to the final Func
var lazy = Activator.CreateInstance(field.FieldType, finalFunc); // now create the Lazy<T>, where T is Square
field.SetValue(this, lazy);
}
}
}
}
Lastly, notice that GetShape was made static. This was for convenience when using Expressions - if you want, you can pass in an instance of ShapeTools to the Expressions code instead.
And as written, ShapeTools.setup is just an example of how this can work. I assume you'd want to clean it up to handle other field types than just Lazy<Shape>.
// var myFunc = Activator.CreateInstance(funcType, () => { return GetShape(shapeType); }) // doesn't compile - type doesn't match
C# is a statically typed language, and as such every type has to be fully known at compile time. As you discovered, while you can ask the runtime to create you any type you build the definition for (with a Type object), the return type has to be fully known -- the function you call returns object for that very reason.
You have a few options here:
Stay on the object level, and use further reflection calls to consume your newly created object. Large performance hit, of course.
Go dynamic, which creates a compiler site to translate any calls through it. It also spreads like a plague, anything that uses dynamic returns dynamic, so until you get something you know the type of you'll be using this kind of calls. Amortized moderate performance hit, very high performance hit during the first JIT compilation.
Use interfaces. You have a base class already, if you define it smart enough, that should be all you need to use your object. That myfunc function would be of type Func<Shape> in that case, and it obviously can only access the Shape portion of your object, though you're free to try casting using is/as as needed, usually in ifs. Very low performance hit, but you need to design your type smart (which you should be doing anyway).
I have a function that returns a particular type. Ex. FaxFile. In that object there are several different formats to download ex: PDF, LargeJpg, SmallJpg, etc.
I can call the download a pdf like this.
public FaxFile DownloadFaxPDFById(int faxId)
{
return DownloadFaxById(faxId).Pdf;
}
What I am wanting to do is be able to pass in the property of the object ex. LargeJpg Format to download.
Sudo Code
public FaxFile DownloadFaxTypeById(int faxId, property)
{
return DownloadFaxById(faxId).property;
}
How do I do this?
you can use reflection.
var resultObj = DownloadFaxById(faxId);
var result = resultObj.GetType().GetProperty("<propertyName>").GetValue(resultObj);
please note that you need to cast result to appropriate object
You don't want to do this at the first place. Just use DownloadFaxById(faxId).Pdf/LargeJpg/...; in the call place or, if you don't want to expose class returned by DownloadFaxById either subclass or use aggregation and expose Pdf, LargeJpg, ... in this new class.
Another way of doing this besides those already posted as answers is to use lambda expressions. Not sure what's the type of the downloaded object, so replace DownloadedObjectType with your own.
public FaxFile DownloadFaxTypeById(int faxId, Expression<Func<DownloadedObjectType, FaxFile>> expression) {
if (!(expression.Body is MemberExpression)) {
throw new ArgumentException("Invalid expression");
}
return expression.Compile().Invoke(DownloadFaxById(faxId));
}
You then call it as
DownloadFaxTypeById(faxId, obj => obj.Pdf)
However looks much uglier than simply calling
DownloadFaxTypeById(faxId).Pdf
Except for maybe providing you some control over what properties can the caller retrieve, limiting them to that specific return type and only those that are actually available for that type. This way for a subset of possible errors (like referencing non-existing property) you get compile time errors rather than all runtime as in cases using reflection.
I would recommend to create an enum for this and pass it to your function.
Inside your function simple use switch.
This means more programming, but the usage of your function is much clearer.
This has also the benefit that you later can change the code how you obtain each format without taking care of the consumer of your function.
enum FaxFileType
{
PDF,
LargeJpg,
SmallJpg,
...
}
public FaxFile DownloadFaxById(int faxId, FaxFileType faxFileType)
{
switch (faxFileType)
{
case FaxFileType.PDF:
return DownloadFaxById(faxId).PDF;
case FaxFileType.LargeJpg:
return DownloadFaxById(faxId).LargeJpg;
case FaxFileType.SmallJpg:
return DownloadFaxById(faxId).SmallJpg;
...
default:
throw new NotSupportedException();
}
}
You should probably just use a switch statement for each format type you support from an enum. However if you really want to use reflection, you can so it as follows:
void Main()
{
var foo = new Foo();
foo.GetType().GetProperty("MyProp").SetValue(foo, "Test");
var val = foo.GetType().GetProperty("MyProp").GetValue(foo);
Console.WriteLine(val);
}
public class Foo
{
public String MyProp { get; set; }
}
Story
I'm trying to write a generic method which combines property names, types and content value to generate a unique string for the value held by the object passed.
The idea is to generate a unique SHA3-512 Hash based on the generated string sequence which can be used to compare objects on generic bases by looking at their content.
Example
Let's say we have a class like this ...
class MyClass {
private Int32 Id = 5;
public String Name = "some string";
protected DateTime CreateDate = DateTime.Parse("2017-08-21 15:00:07");
}
... and the mentioned method to generate the unique string
static String GetContentString<T>(T obj) where T : class {
...
}
In theory this should work somewhat like this:
var myObj = new MyClass();
var uniqueContentString = GetContentString(myObj);
Console.WriteLine(uniqueContentString);
>>> Id:Int32:5$Name:String:some string$CreateDate:DateTime:2017-08-21 15:00:07
Problem
I'm having difficulties building the GetContentString Method. This is what I have already:
Object obj = ... // given object
Type type = obj.GetType();
IList<PropertyInfo> propertyInfos = type.GetProperties().Where(x => x.CanRead).ToList(); // Marker #2
StringBuilder sb = new StringBuilder();
foreach (PropertyInfo pi in propertyInfos)
{
sb.Append(pi.Name);
sb.Append(":");
sb.Append(pi.PropertyType.Name);
sb.Append(":");
sb.Append(pi.GetValue(obj) ?? "[ISNULL]"); // Marker #1
sb.Append(":");
}
return sb.ToString();
I tried running the method for a few different types of values like "some string" or 1234 (Int32) and ran into a few issues.
Given a string, the method call throws an exception of type System.Reflection.TargetParameterCountException and the message Parameter count mismatch at #1. I found out that an optional index can be passed to an overloaded version of pi.GetValue(..) which then returns one of the single letters. But how do you know when to stop? If you call an index which doesn't exist it throwns an exception of the type System.Reflection.TargetInvocationException. How do you get the value of a string object using reflection?
Given an integer value, the method call doesn't find any properties at #2. Which brings up the question of how to get the value of an integer object using reflection?
And also some general questions; do you guys think this is a good approach to get a unique string? Is reflection the way to go here? Is it even possible to write a generic solution to this problem?
Without looking at reflection, how about JSON serialization with something that .net framework is able to ?
Reflection isn't something extremely fast and you'll run into issues at the first unhandled exception.
Then, you should do that recursivly if your objects can contains complex properties, wich is not a problem with json serialization !
Is there a way in .NET to know what parameters and their values were passed to a method. Reflection way? This will be used from inside the method. It has to be generic so it can be used from any method. This is for logging purposes.
Call MethodBase.GetCurrentMethod().GetParameters().
However, it is not possible to get the parameter values; due to JIT optimization, they might not even exist anymore.
MethodInfo.GetCurrentMethod() will give you information about the current method and then get information about the parameters using GetParameters().
What you're trying to do can be achieved easily using aspect oriented programming. There are good tutorials online, I'll point to two of them:
http://ayende.com/Blog/archive/2008/07/31/Logging--the-AOP-way.aspx
http://www.codeproject.com/KB/cs/UsingAOPInCSharp.aspx
public void FunctionWithParameters(string message, string operationName = null, string subscriptionId = null)
{
var parameters = System.Reflection.MethodBase.GetCurrentMethod().GetParameters();
this.PrintParams(parameters, message, operationName, subscriptionId);
}
public void PrintParams(ParameterInfo[] paramNames, params object[] args)
{
for (int i = 0; i < args.Length; i++)
{
Console.WriteLine($"{paramNames[i].Name} : {args[i]}");
}
}
You need AOP to achieve what you are looking for.
in c# you can use DispatchProxy to do that.
Check the following How to wrap existing object instance into DispatchProxy?
Nowadays a feature that could be used to achieve this is Roslyn's Source Generators.
In this way, the code that gets the parameters' values would be generated at compile-time based on the method definition. Could be interpreted as “compile-time reflection”.
Let's show an example to try to explain it better:
public void MethodInspectingItsOwnParameters(
[Description("First parameter")]
string paramName_1,
[Description("Second parameter")]
int paramName_2,
// ...
[Description("N-th parameter")]
object paramName_N,
// ...
[Description("Last parameter")]
bool paramName_M)
{
var paramsAndValues = new List<KeyValuePair<string, object>>();
// -
// => Put here the code that, using Roslyn's compile time
// metaprogramming, inspect the parameters from the method
// definition and at compile time generate the code that
// at run time will get the parameters's values and load
// them into [paramsAndValues]
// -
// #Rosalyn generated code
// · Code autogenerated at compile time
// => loads parameter's values into [paramsAndValues]
// -
// Eg (Hypothetical example of the code generated at compile
// time by Roslyn's metaprogramming):
//
// paramsAndValues.Add("paramName_0", paramName_1);
// ...
// paramsAndValues.Add("paramName_N", paramName_N);
// ...
// paramsAndValues.Add("paramName_M", paramName_M);
//
// - Note: this code will be regenerated with each compilation,
// so there no make sense to do nameof(paramName_N)
// to obtaint parameter's name
// #End Rosalyn generated code
foreach (var param in paramsAndValues)
{
string paramName = param.Key;
object paramValue = param.Value;
// In this section of the normal code (not generated at
// compile time) do what you require with the
// parameters/values already loaded into [paramsAndValues]
// by the compile time generated code
}
}
I have the following code
var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(TestType) },
typeof(Program).Module, false);
var add2Body = typeof(Program).GetMethod("add2").GetMethodBody().GetILAsByteArray();
var dynamicIlInfo = dynamicAdd2.GetDynamicILInfo();
var ilGenerator = dynamicAdd2.GetILGenerator();
dynamicIlInfo.SetLocalSignature(SignatureHelper.GetLocalVarSigHelper().GetSignature());
dynamicIlInfo.SetCode(add2Body, 1000);
var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>));
var ret2 = test2(new TestType()); // <-- Exception
the add2:
public string add2(TestType digit)
{
return digit.Name;
}
the testType:
public class TestType
{
public string Name = "test";
}
I get a InvalidProgrammException, no more information
So I expect that the creation of the dynamic method fails. I think the dynamic Method can not find the references to the TestClass. Or what can be wrong in this case? Or what can I do to get a hint where the problem lies? the Exception brings not the needed infos...
You cannot directly copy IL stream from existing method to dynamic method, because IL uses so called tokens (32-bit numbers) to represent types, methods or fields. For the same field, value of token can be different in different modules, so byte-copying method IL stream without replacing tokens results in invalid program.
Second problem is that because add2 is instance method (not static), you must add instance of type that this method belongs to as first argument of method. In C# this first argument of instance methods is hidden, but IL requires it. Or you can declare method as static to avoid this.
Third problem is that add2 method contains (compiler generated) local variable. You have to add this variable to local signature (using SetLocalSignature() method), otherwise your method would use undeclared variable. (See code bellow to see how to do that).
Solution 1:
First solution is to use GetILGenerator() instead of GetDynamicILInfo(), and rewrite IL stream from scratch. You can use IL disassembler (e.g. ILDASM, .NET Reflector) to get list of instructions for any existing method. Writing these instructions to IlGenerator using IlGenerator.Emit(...) should not be difficult.
static void Main(string[] args)
{
var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(Program), typeof(TestType) },
typeof(Program).Module,
false);
var ilGenerator = dynamicAdd2.GetILGenerator();
ilGenerator.DeclareLocal(typeof(string));
ilGenerator.Emit(OpCodes.Ldarg_1);
var fld = typeof(TestType).GetField("Name");
ilGenerator.Emit(OpCodes.Ldfld, fld);
ilGenerator.Emit(OpCodes.Ret);
var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>), new Program());
var ret2 = test2(new TestType());
}
Solution 2:
If you cannot use IlGenerator and you require direct IL stream manipulation using GetDynamicILInfo, you have to replace tokens in IL stream with values that are valid for generated dynamic method. Replacing tokens generally requires you to know offsets of these tokens in IL stream. Problem is that exact offset depends on compiler (and is even different for Release/Debug build). So you either have to use some IL dissassembler to get these offsets, or write IL parser able to do that (which is not trivial, maybe you can find some library for that). So following code uses kind of "dirty hack" to make it work in this particular case, but does not work generally.
public static void Main()
{
var dynamicAdd2 = new DynamicMethod("add",
typeof(string),
new[] { typeof(Program), typeof(TestType) },
typeof(Program).Module,
false);
var add2Body = typeof(Program).GetMethod("add2").GetMethodBody();
var add2ILStream = add2Body.GetILAsByteArray();
var dynamicIlInfo = dynamicAdd2.GetDynamicILInfo();
var token = dynamicIlInfo.GetTokenFor(typeof(TestType).GetField("Name").FieldHandle);
var tokenBytes = BitConverter.GetBytes(token);
//This tries to find index of token used by ldfld by searching for it's opcode (0x7B) in IL stream.
//Token follows this instructions so I add +1. This works well for this simple method, but
//will not work in general case, because IL stream could contain 0x7B on other unrelated places.
var tokenIndex = add2ILStream.ToList().IndexOf(0x7b) + 1;
Array.Copy(tokenBytes, 0, add2ILStream, tokenIndex, 4);//
//Copy signature of local variables from original add2 method
var localSignature = SignatureHelper.GetLocalVarSigHelper();
var localVarTypes = add2Body.LocalVariables.Select(_ => _.LocalType).ToArray();
localSignature.AddArguments(localVarTypes, null, null);
dynamicIlInfo.SetLocalSignature(localSignature.GetSignature());
dynamicIlInfo.SetCode(add2ILStream, 1);
var test2 = (Func<TestType, string>)dynamicAdd2.CreateDelegate(typeof(Func<TestType, string>));
var ret2 = test2(new TestType());
}