Call method that has a Delegate instance parameter through Reflection C# - c#

I have this C# code in an external assembly:
namespace Fancy
{
internal class Foo
{
public static void Window(string title, Foo.WindowFunction sceneViewFunc, int order)
{}
public delegate void WindowFunction(float x, float y);
}
}
And I have my code:
class A
{
public static void Draw(float x, float y)
{
// impl
}
static void Main(string[] args)
{
var newWindow = ?;
}
}
I want to call Fancy.Foo.Window() like
Fancy.Foo.Window("Window Name", new Foo.WindowFunction(A.Draw), 450);
inside my A class through Reflection.
How can I do it? Tried lot of different options no success :/

The documentation defines internal access modifier as follows
The type or member can be accessed by any code in the same assembly, but not from another assembly.
Not being able to access from outside of the assembly is the default and expected behaviour. But you can do that in multiple ways. If you have access to the source of the external assembly. You can mark your current assembly as a friend of the external assembly as shown below and recmpile it.
[assembly: InternalsVisibleTo("<Your assembly name>")]
Considering that you might not always have access to the source of external assembly, the same can be done using reflection by loading the assembly, creating an instance of the class and fetching the Non-Public members on the internal type. See how to access internal class using reflection. See also other ways to use internal class of another assembly.
Using reflection on external/third-party assemblies comes with its own caveats as the source of the assembly may change at any point in time, breaking your code.

Below is the code:
ClassLibrary1:
using System;
namespace ClassLibrary1
{
public class Foo
{
public static void Window(string title, WindowFunction sceneViewFunc, int order)
{
Console.WriteLine("Foo Window");
}
public delegate void WindowFunction(float x, float y);
}
}
Main Program.cs
using System;
using static ClassLibrary1.Foo;
namespace Algorithums
{
public class Program
{
public static void Draw(float x, float y)
{
// impl
}
public static void Main(string[] args)
{
RegisterWindowFunctionActionAndWindow("Window Name", 450);
Console.WriteLine("Hello World!");
Console.ReadLine();
}
public static void RegisterWindowFunctionActionAndWindow(string WindowName, int order)
{
var jsoType = Type.GetType("ClassLibrary1.Foo,ClassLibrary1");
var jso = Activator.CreateInstance(jsoType);
var mi = typeof(Program).GetMethod("Draw");
var d = Delegate.CreateDelegate(typeof(WindowFunction), mi);
var mi0 = jsoType.GetMethod("Window", new[] { typeof(string), typeof(WindowFunction), typeof(int) });
mi0.Invoke(jso, new object[] { WindowName, d, order });
}
}

Related

"AfterRead" event in the v2.0.0 version

My goal is to change the value of a field after it is read. And I think it is possible running the "AfterRead" event.
My current version of the library is 2.0.0.
I have tested the next getting an error:
public class sealed MyClass: INotifyRead
{
// ...
public void AfterRead(EngineBase engine, AfterReadEventArgs e)
{
}
}
The error message is:
Cannot find the namespace "AfterReadEventArgs". Missing the "using" directive or assembly reference.
I have read the next code in the docs:
FileHelperEngine engine = new FileHelperEngine(typeof(Orders));
engine.BeforeReadRecord += new BeforeReadRecordHandler(BeforeEvent);
Orders[] res = engine.ReadFile("report.txt") as Orders[];
I don't know if it is needed to declare the delegate in the source code or it is enough the declaration of the event in the Mapping Class.
Thanks a lot!
The signature of the AfterRead event is different in 2.0.0.0. Here's a working example.
using System;
using System.Diagnostics;
using FileHelpers;
namespace ConsoleApp
{
[DelimitedRecord(";")]
public sealed class MyClass : INotifyRead
{
public string Field1;
public string Field2;
public void AfterRead(EngineBase engine, string line)
{
Field1 = "ModifiedValue1";
}
}
internal class Program
{
static void Main(string[] args)
{
FileHelperEngine engine = new FileHelperEngine(typeof(MyClass));
var records = engine.ReadString("Value1;Value2");
var firstRecord = records[0] as MyClass;
Debug.Assert(firstRecord.Field1 == "ModifiedValue1");
Console.Write("All OK. Field1 had the modified value.");
Console.ReadKey();
}
}
}

Automatically passing own type to function call

I'd like to pass a value when calling a function but want to omit to actually add it as a parameter.
To be more precise I'd like to write a logger that also prints which class called the logging function but don't want to always pass a "this" as a parameter.
Example code:
class static Logger{
public static void LogMsg(string msg, object objectCalling){
Print(objectCalling.GetType().Name + ": " + msg);
}
private void Print(string msg){
// print it
}
}
class SomeClass{
private void WriteTestLog() {
Logger.LogMsg("Testing!");
}
}
This should then create an output like: "SomeClass: Testing!"
I am not sure how to tackle this maybe I am just missing sth.
There are a few attributes which might be helpful:
CallerMemberNameAttribute: the name of the calling method or property;
CallerFilePathAttribute: the file path where the calling member is in;
CallerLineNumberAttribute: the line number within the file.
As you see, there is no attribute for the class name, but with the file path you might achieve the same level of information.
How to use this? Decorate an argument in your logging method with the attribute (of course, using the correct type and default).
public static void LogMsg(string msg, [CallerMemberName] string callingMember = null)
{
Print($"{callingMember}: {msg}");
}
And just call:
LogMsg("hello!");
You can use System.Runtime.CompilerServices with it's CallerMemberNameAttribute
Here is example:
using System;
using System.Runtime.CompilerServices;
namespace ConsoleApp3
{
class Program
{
static void Main(string[] args)
{
Logger.WriteLog("Hello");
}
}
public class Logger
{
public static void WriteLog(string msg, [CallerMemberName] string methodName="")
{
Console.WriteLine("Method:{0}, Message: {1}",methodName,msg);
}
}
}
You could create an extension method to do that as well as the answers above :
void Main()
{
SomeClass x = new SomeClass();
x.WriteTestLog();
int i = 1;
i.LogMsg("abc");
}
public static class Logger
{
public static void LogMsg(this object objectCalling, string msg)
{
Print(objectCalling.GetType().Name + ": " + msg);
}
private static void Print(string msg)
{
Console.WriteLine(msg); // print it
}
}
public class SomeClass
{
public void WriteTestLog()
{
this.LogMsg("Testing!");
}
}
If you really wish to extract the caller's type you can play with the stack trace:
[MethodImpl(MethodImplOptions.NoInlining)]
public static void LogMsg(string msg)
{
var caller = new StackTrace().GetFrames()[1].GetMethod();
Console.WriteLine($"{caller.DeclaringType}.{caller.Name}: {msg}");
}
But keep in mind that extracting the caller from the stack trace is a very expensive operation. Besides despite of the NoInlining it is not guaranteed that in an optimized build the caller itself is not inlined. I do not recommend to use it in a release build or if performance matters.

Why can extension method not overwrite original object?

It seems that an extension method in C# cannot overwrite the original object. Why is that? Example:
using System;
namespace ExtensionTest
{
public class MyTest {
public string MyName { get; set; }
}
class Program
{
static void Main(string[] args)
{
var myTest = new MyTest() { MyName = "Arne" };
Console.WriteLine("My name is {0}", myTest.MyName);
// Will write "My name is Arne"
myTest.AlterMyTest();
Console.WriteLine("My name is {0}", myTest.MyName);
// Will write "My name is Bertil"
myTest.OverwriteMyTest();
Console.WriteLine("My name is {0}", myTest.MyName);
// Will write "My name is Bertil" (why?)
}
}
public static class ExtensionClass{
public static void AlterMyTest(this MyTest myTest)
{
myTest.MyName = "Bertil";
}
public static void OverwriteMyTest(this MyTest myTest)
{
myTest = new MyTest() { MyName = "Carl" };
}
}
}
Because as usual, reference of the class is copied while passing to the method, and you are assigning new object to the new reference.
For not-extension methods, you can pass reference by ref/out keywords
public static void Func(out MyClass b)
{
b = new MyClass();
}
...
MyClass b;
Func(out b);
Assert.IsNotNull(b);
but C# compiler doesn't allow to use ref with this(the reason is in David Arno's comment). You are free to remove this keyword, and call static method instead of extension.

Alias for static member in C#?

I have a static member:
namespace MyLibrary
{
public static class MyClass
{
public static string MyMember;
}
}
which I want to access like this:
using MyLibrary;
namespace MyApp
{
class Program
{
static void Main(string[] args)
{
MyMember = "Some value.";
}
}
}
How do make MyMember accessible (without MyClass.) to MyApp just by adding using MyLibrary?
C# doesn't allow you to create aliases of members, only of types. So the only way to do something like that in C# would be to create a new property which is accessible from that scope:
class Program
{
static string MyMember
{
get { return MyClass.MyMember; }
set { MyClass.MyMember = value; }
}
static void Main(string[] args)
{
MyMember = "Some value.";
}
}
It's not really an alias, but it accomplishes the syntax you're looking for.
Of course, if you're only accessing / modifying a member on MyClass, and not assigning to it, this can be simplified a bit:
class Program
{
static List<string> MyList = MyClass.MyList;
static void Main(string[] args)
{
MyList.Add("Some value.");
}
}

Aluminum Lua - register function

How do you register Lua method info (of static public void) to a context in AluminiumLua?
You can achieve this by providing a delegate matching your method's signature.
using System;
using AluminumLua;
public delegate void HelloDelegate();
class Program
{
public static void Hello()
{
Console.Write("Hello world!");
}
static void Main()
{
var context = new LuaContext();
var obj = LuaObject.FromDelegate(new HelloDelegate(Hello));
context.SetGlobal("hello", obj);
context.Get("hello").AsFunction().Invoke(new LuaObject[] { });
}
}

Categories

Resources