I'm trying to implement my own solution for logging. Everything works fine except one thing, that just doesn't want to work out.
I have a class (Log), which has methods to log to file etc.
I can use it like Log.Debug(message, args), but thats not enough for me.
Sadly in C# we can't overload the call operator to be able to do something like Log(message, args).
Therefore I've searched on the web and found out about the indexer.
My idea would be now to do something like:
Log[loggingMode](message, args).
But I just can't get it working. I currently have delegate, a method and the indexer, which look like this:
/// <summary>
/// Delegate for logging function, used by the indexer.
/// </summary>
/// <param name="mode">The logging mode.</param>
/// <param name="message">The message to log.</param>
/// <param name="args">The args for the message (String.Format).</param>
public delegate void LogDelegate(string mode, string message, params object[] args);
public LogDelegate this[string mode]
{
get
{
return LogIndexer;
}
}
public void LogIndexer(string mode, string message, params object[] args)
{
lock (_Lock)
{
_queue.Enqueue(new LogEntry(String.Format(message, args), mode));
}
}
Now my question is, how can I pass the one argument of the indexer (mode) to the function, so that I can call it like:
Log"debug";
this[string mode] getter should use its mode parameter to return a lambda with this mode:
public delegate void LogDelegate(string message, params object[] args);
public LogDelegate this[string mode]
{
get
{
return (message, args) => LogIndexer(mode, message, args);
}
}
Related
I have a sort of message broker class that maps incoming message types to handler methods at runtime. It is important for me to maintain strongly typed messages in the handlers. It works by finding all classes that inherit from IMessage and creating a Delegate to invoke any method that has a Handles(TMessage) attribute on it, where TMessage matches the incoming IMessage underlying Type.
So a "handler" looks like this:
[Handles(typeof(TestMessage))]
public void HandleTestMessage(objectsender, TestMessage request)
{
var response = new TestResponse() { TestInt = request.TestInt };
msgService.Send(sender, response);
}
Again, I'd really like to avoid having an IMessage in the method signature, it's important to me to not have to cast the IMessage in the method body.
The broker class uses the following method to discover and register the handlers:
/// <summary>
/// Subscribes all methods with the <see cref="HandlesAttribute"/> to the given <see cref="IMessage"/> <see cref="Type"/>
/// </summary>
/// <param name="target">The object to inspect</param>
public void SubscribeAll(object target)
{
var targetType = target.GetType();
// Get all private and public methods.
var methods = targetType.GetMethods(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance);
foreach (var method in methods)
{
// If this method doesn't have the Handles attribute then ignore it.
var handlesAttributes = (HandlesAttribute[])method.GetCustomAttributes(typeof(HandlesAttribute), false);
if (handlesAttributes.Length != 1)
continue;
// The method must have only 2 arguments.
var parameters = method.GetParameters();
if (parameters.Length != 2)
{
log.LogDebug(string.Format("Method {0} has too many arguments", method.Name));
continue;
}
// The second argument must be derived from IMessage.
if (!typeof(IMessage).IsAssignableFrom(parameters[1].ParameterType))
{
log.LogDebug(string.Format("Method {0} does not have an IMessage as it's second argument", method.Name));
continue;
}
Type genericDelegate;
if(method.ReturnType == typeof(void))
{
genericDelegate = typeof(Action<,>).MakeGenericType(parameters[0].ParameterType, handlesAttributes[0].MessageType);
}
else
{
genericDelegate = typeof(Func<,,>).MakeGenericType(parameters[0].ParameterType, handlesAttributes[0].MessageType, method.ReturnType);
}
var handler = method.CreateDelegate(genericDelegate, target);
// Success, so register!
Subscribe(
handlesAttributes[0].MessageType,
handler);
}
}
and finally when a message is received, it uses this method to invoke (DynamicInvoke) the handler:
/// <summary>
/// Passes a given <see cref="IMessage"/> and optional sender to any <see cref="Handler"/>s accepting the message's underlying type
/// </summary>
/// <param name="message">The <see cref="IMessage"/> to send</param>
/// <param name="sender">The original sender of the message</param>
private void HandleMessage(IMessage message, object sender = null)
{
var messageType = message.GetType();
if (messageHandlers.TryGetValue(messageType, out List<Delegate> handlers))
{
foreach (var handler in handlers)
{
handler.DynamicInvoke(new object[] { sender, message });
}
}
else
{
log.LogError(string.Format("No handler found for message of type {0}", messageType.FullName));
throw new NoHandlersException();
}
}
I thought I was being smart when I converted the MethodInfos into Delegates (Action<,> or Func<,,>) but I neglected to check the implications of using DynamicInvoke over Invoke. It turns out DynamicInvoke incurs a comparatively large amount of overhead. So my question is, how can I treat the handler Delegates as the actual Delegates that they are (again, Action<,> or Func<,,>)
As you can see, in HandleMessage, I do know the underlying type of message, and sender is always an object. Basically, I need to do something like:
((Action<object, typeof(message)>)handler).Invoke(sender, message);
Obviously that's impossible, but it illustrates what I am trying to accomplish clearly (I think).
I tried creating a sort of in-between generic method:
public void InvokeHandler<TMessage>(Action<object, TMessage> handler, TMessage message, object sender = null)
But I just run into contravariance problems down the line when trying to invoke this method.
I would like to add a completion suggestion of nameof to a parameter in a method.
The method:
public static class Ensure
{
/// <summary>
/// throws <see cref="ArgumentException"/> exception if string is null or empty.
/// </summary>
/// <param name="value">string to check its content.</param>
/// <param name="name">name of parameter passed.</param>
public static void StringNotNullOrEmpty(string value, string name)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty", name);
}
}
}
When typing Ensure.StringNotNullOrEmpty(testString, <...>) I would like to get the nameof(testString) suggestion for <...>. The above method doesn't suggest the nameof (Worse still, after typing 'n' I only get suggestions as null, new). The nameof(...) is necessary so I get the correct ParamName in the ArgumentException.
On the site of Resharper it says that ArgumentNullException makes use of the nameof suggestion by using the InvokerParameterNameAttribute (source: Resharper InvokerParameterNameAttribute).
Attempt:
So I installed the nuget package in my project: JetBrains.Annotations 10.4.0 and changed the method as followed:
public static class Ensure
{
/// <summary>
/// throws <see cref="ArgumentException"/> exception if string is null or empty.
/// </summary>
/// <param name="value">string to check its content.</param>
/// <param name="name">name of parameter passed.</param>
public static void StringNotNullOrEmpty([CanBeNull] string value,
[NotNull] [InvokerParameterName] string name)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty", name);
}
}
}
Unfortunately the above attempt is still not giving me the suggestion of nameof when I've typed: Ensure.StringIsNotNullOrEmpty(testString,.
Some other thing I found was the CallerMemberNameAttribute on MSDN. But this only gives me the name of the calling method as a string.
I also looked into some repo's on GitHub, and could only find solutions the same way as my attempt.
One reason I can think of is when having two string parameters in a method with an InvokerParameterNameAttribute defined on the latter, it could be confusing for Resharper.
Is it possible to add the nameof suggestion for a parameter in a method? And how can I achieve this with the above method?
Additional info:
I have VS2015 + Resharper 2016.3.2.
actually that works (but not as you intended). it will not suggest as soon as you type ,... it will only suggest you to use nameof expression when you fully type name of argument in form of string instead of using nameof expression.
static void Caller(string arg)
{
StringNotNullOrEmpty(arg, "arg"); // only now resharper suggests to use nameof.
StringNotNullOrEmpty(arg, "something else"); // now resharper complains about unknown argument name.
}
You can submit feedback on resharper to actually implement the behavior you want.
Also if you type "" the cursor will move to middle of double quotes and resharper suggests name of available parameters.
BTW you can always comment your code that can be useful for every future readers
/// <summary>
/// throws <see cref="ArgumentException"/> exception if string is null or empty.
/// </summary>
/// <param name="value">string to check its content.</param>
/// <param name="name">name of parameter passed.</param>
public static void StringNotNullOrEmpty([CanBeNull] string value,
[NotNull] [InvokerParameterName] string name)
{
if (string.IsNullOrEmpty(value))
{
throw new ArgumentException("Value cannot be null or empty", name);
}
}
I'm trying to implement managed debugger looking at MDBG sample.
MDBG is capable of resolving function names within given scope, but it's not taking in consideration base classes.
MDBG is doing this:
/// <summary>
/// Resolves a Function from a Module, Class Name, and Function Name.
/// </summary>
/// <param name="mdbgModule">The Module that has the Function.</param>
/// <param name="className">The name of the Class that has the Function.</param>
/// <param name="functionName">The name of the Function.</param>
/// <returns>The MDbgFunction that matches the given parameters.</returns>
public MDbgFunction ResolveFunctionName(MDbgModule mdbgModule, string className, string functionName) {
...
foreach (MethodInfo mi in t.GetMethods()) {
if (mi.Name.Equals(functionName)) {
func = mdbgModule.GetFunction((mi as MetadataMethodInfo).MetadataToken);
break;
}
}
return func;
}
While the Type.GetMethods() is overriden and has this implementation, using IMetaDataImport.EnumMethods:
public override MethodInfo[] GetMethods(BindingFlags bindingAttr) {
ArrayList al = new ArrayList();
IntPtr hEnum = new IntPtr();
int methodToken;
try {
while (true) {
int size;
m_importer.EnumMethods(ref hEnum, (int) m_typeToken, out methodToken, 1, out size);
if (size == 0) {
break;
}
al.Add(new MetadataMethodInfo(m_importer, methodToken));
}
}
finally {
m_importer.CloseEnum(hEnum);
}
return (MethodInfo[]) al.ToArray(typeof (MethodInfo));
}
The problem is that m_importer.EnumMethods() Enumerates MethodDef tokens representing methods of the specified type, but I'm interested in all methods from the class hierarchy.
How can I get all the Methods defined in class hierarchy? (Obviously, common methods like reflection cannot be used, since I'm analyzing type defined in other process)
My limited knowledge of interop and deep CLR/CIL structure creates impediments for finding the right way to go here.
Any advice/suggestion is welcome!
Regards,
GetTypeProps will return the metadata token of the base type in ptkExtends, you can use that to walk up the inheritance tree and collect the methods from each as you go.
Be aware, however, that the metadata token might not be a TypeDef. It could be a TypeRef (requiring you to resolve the type) or a TypeSpec (requiring you to parse the type signature and extract an appropriate TypeDef/TypeRef).
I have a Dictionary containing strings as keys, and objects as values in an abstract class.
I have two classes deriving from this abstract class.
One of the deriving classes works perfectly, all configurations and items are loaded and retrievable without issues.
However, the other class is giving me headaches.
When I try to get an object of type "Domain"; I get an invalid cast exception, although I am adding the value to the dictionary as said type.
Here is the code:
public sealed class UserDomainConfig: ConfigParser {
public UserDomainConfig(string configFilePath) : base(configFilePath) { }
public Domain GetConfig(string key) => GetConfig<Domain>(key);
public override bool LoadConfigs() {
return base.LoadConfigs();
}
public UserDomainConfig SetConfig(string key, Domain value) {
base.SetConfig(key, value);
return this;
}
}
public abstract class ConfigParser: IConfig {
/* Snip */
/// <summary>
/// Gets the config.
/// </summary>
/// <returns>The config.</returns>
/// <param name="key">Key.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public virtual T GetConfig<T>(string key) {
object output = null;
try {
if (!configs.TryGetValue(key, out output))
return default(T);
//return (T)output;
//return output as T;
// This is where the exception is occurring.
// I've tried multiple solutions to try to remedy this issue.
return (T)Convert.ChangeType(output, typeof(T));
} catch (InvalidCastException ex) {
logger.Error($"Failed to cast config { key }!");
}
return default(T);
}
/// <summary>
/// Sets the config.
/// </summary>
/// <returns>The config.</returns>
/// <param name="key">Key.</param>
/// <param name="value">Value.</param>
/// <typeparam name="T">The 1st type parameter.</typeparam>
public virtual IConfig SetConfig<T>(string key, T value) {
if (KeyExists(key))
configs.Remove(key);
configs.Add(key, value);
return this;
}
Any ideas on how to fix this, and/or why this isn't working in the first place, although it works like a charm with strings, bools, and ints?
The Convert class only supports simple types, known by .NET, like Int32, String, DateTime. It does not support user defined or complex types like Domain. If you try to convert something to a not-supported type, the method Convert.ChangeType throws an InvalidCastException. The only exception is that it will work if the Original value (output) is already of that desired type; than no actual conversion is needed.
For more information, read: https://msdn.microsoft.com/en-us/library/dtb69x08(v=vs.110).aspx
If you are certain the stored value is of the type Domain, than log more information. Something like this:
logger.Error($"Failed to cast config { key } of type { output.GetType() } to type { typeof(T) }!");
This way you can verify your claim that both types are the same.
I have a class with a private static method with an optional parameter. How do I invoke it from another class via Reflection? There is a similar question, but it does not address static method or optional parameters.
public class Foo {
private static void Bar(string key = "") {
// do stuff
}
}
How do I invoke Foo.Bar("test") and Foo.Bar() (e.g. without passing the optional parameter)?
Optional parameter values in C# are compiled by injection those values at the callsite. I.e. even though your code is
Foo.Bar()
The compiler actually generates a call like
Foo.Bar("")
When finding the method you need to treat the optional parameters as regular parameters.
var method = typeof(Foo).GetMethod("Bar", BindingFlags.Static | BindingFlags.NonPublic);
If you know exactly what values you want to invoke the method with you can do:
method.Invoke(obj: null, parameters: new object[] { "Test" });
If you only have some of the parameters and want to honor the values of the default ones you have to inspect the method's ParameterInfo objects to see if the parameters are optional and what those values are. For example to print out the default values of those parameters you could use the following code:
foreach (ParameterInfo pi in method.GetParameters())
{
if (pi.IsOptional)
{
Console.WriteLine(pi.Name + ": " + pi.DefaultValue);
}
}
Using this class
public class Foo
{
private static void Bar(string key = "undefined key", string value = "undefined value")
{
Console.WriteLine(string.Format("The key is '{0}'", key));
Console.WriteLine(string.Format("The value is '{0}'", value));
}
}
You can use the following code to call it with the default values
MethodInfo mi = typeof(Foo).GetMethod("Bar", BindingFlags.NonPublic | BindingFlags.Static);
ParameterInfo[] pis = mi.GetParameters();
object[] parameters = new object[pis.Length];
for (int i = 0; i < pis.Length; i++)
{
if (pis[i].IsOptional)
{
parameters[i] = pis[i].DefaultValue;
}
}
mi.Invoke(null, parameters);
If the method had some non-optional parameters, you will have to insert them into the parameters array before invoking the method.
E.g
private static void Bar(int number, string key = "undefined key", string value = "undefined")
Would require you to do
parameters[0] = "23"
Before invoking
Something i wrote for my unit tests:
/// <summary>
/// Attempts to execute a function and provide the result value against the provided source object even if it is private and/or static. Just make sure to provide the correct BindingFlags to correctly identify the function.
/// </summary>
/// <typeparam name="TReturn">The expected return type of the private method.</typeparam>
/// <param name="type">The object's Type that contains the private method.</param>
/// <param name="source">The object that contains the function to invoke. If looking for a static function, you can provide "null".</param>
/// <param name="methodName">The name of the private method to run.</param>
/// <param name="flags">Binding flags used to search for the function. Example: (BindingFlags.NonPublic | BindingFlags.Static) finds a private static method.</param>
/// <param name="output">The invoked function's return value.</param>
/// <param name="methodArgs">The arguments to pass into the private method.</param>
/// <returns>Returns true if function was found and invoked. False if function was not found.</returns>
private static bool TryInvokeMethod<TReturn>(Type type, object source, string methodName, BindingFlags flags, out TReturn output, params object[] methodArgs)
{
var method = type.GetMethod(methodName, flags);
if(method != null)
{
output = (TReturn)method.Invoke(source, methodArgs);
return true;
}
// Perform some recursion to walk the inheritance.
if(type.BaseType != null)
{
return TryInvokeMethod(type.BaseType, source, methodName, flags, out output, methodArgs);
}
output = default(TReturn);
return false;
}
Then call it like so to invoke a private static function:
var success = TryInvokeMethod(typeof(Foo), null, "MyPrivateStaticFunc", BindingFlags.NonPublic | BindingFlags.Static, out result, arg1ToPass);
Disclaimer: I use this for functions with a return value only. Attempting to execute a method with no return value will throw an exception.