The attribute which targets method is not working. The code is below. What could be the problem?
using System;
namespace AttributeProgram
{
class Program:ContextBoundObject
{
[TestAttribute("Hello")]
public void Print()
{
Console.WriteLine("How are you?");
}
static void Main(string[] args)
{
Program obj = new Program();
obj.Print();
}
}
[AttributeUsage(AttributeTargets.Method)]
class TestAttribute : System.Runtime.Remoting.Contexts.ContextAttribute
{
public TestAttribute(string Name) : base("Test")
{
Console.WriteLine(Name);
}
}
}
Because you're inheriting from ContextAttribute which can be applied only to classes, as per documentation:
[SerializableAttribute]
[ComVisibleAttribute(true)]
[AttributeUsageAttribute(AttributeTargets.Class)]
[SecurityPermissionAttribute(SecurityAction.LinkDemand, Flags = SecurityPermissionFlag.Infrastructure)]
[SecurityPermissionAttribute(SecurityAction.InheritanceDemand, Flags = SecurityPermissionFlag.Infrastructure)]
public class ContextAttribute : Attribute,
IContextAttribute, IContextProperty
Related
In this example, I want to patch PatchTarget.QSingleton\<T\>.get_Instance().
How to get it done with Harmony or MonoMod?
Harmony:
"Unhandled exception. System.NotSupportedException: Specified method
is not supported."
MonoMod:
"Unhandled exception. System.ArgumentException: The given generic
instantiation was invalid."
Code snippet: (runnable with dotnetfiddle.net)
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Reflection.Emit;
using HarmonyLib;
namespace PatchTarget {
public abstract class QSingleton<T> where T : QSingleton<T>, new() {
protected static T instance = null; protected QSingleton() { }
public static T Instance { get {
if (instance == null) {
instance = new T();
Console.Write($"{typeof(T).Name}.Instance: impl=QSingleton");
}
return instance;
} }
}
}
namespace Patch {
public class TypeHelper<T> where T : PatchTarget.QSingleton<T>, new() {
public static T InstanceHack() {
Console.Write($"{typeof(T).Name}.Instance: impl=InstanceHack");
return null;
}
}
public static class HarmonyPatch {
public static Harmony harmony = new Harmony("Try");
public static void init() {
var miOriginal = AccessTools.Property(typeof(PatchTarget.QSingleton<>), "Instance").GetMethod;
var miHack = AccessTools.Method(typeof(TypeHelper<>), "InstanceHack");
harmony.Patch(miOriginal, prefix: new HarmonyMethod(miHack));
}
}
public static class MonoModPatch {
public static MonoMod.RuntimeDetour.Detour sHook;
public static void init() {
var miOriginal = AccessTools.Property(typeof(PatchTarget.QSingleton<>), "Instance").GetMethod;
var miHack = AccessTools.Method(typeof(TypeHelper<>), "InstanceHack");
sHook = new MonoMod.RuntimeDetour.Detour(miOriginal, miHack);
}
}
}
class Program {
public static void Main() {
Patch.HarmonyPatch.init();
// Patch.MonoModPatch.init();
Console.WriteLine($"done");
}
}
After some trial and error, I got something working, but not the reason behind it.
Both Harmony and MonoMod.RuntimeDetour can hook with the typeof(QSingleton<SampleA>).GetMethod(), but not typeof(QSingleton<>).GetMethod().
Harmony output is unexpected.
Harmony attribute annotation doesn't seem to work.
Generating IL seems useless due to the potential lack of TypeSpec for generic.
Questions:
What is the difference between QSingleton<>.Instance and QSingleton<SampleA>.Instance in the sample?
I would guess that <>.Instance is MethodDef, while <SampleA>.Instance is TypeSpec.MemberRef.
Why does Harmony/MonoMod.RuntimeDetour need TypeSpec.MemberRef? For generating redirection stub?
Is it possible to fix the hook under Harmony?
Can Harmony/MonoMod generates "ldtoken <TypeSpec>" if TypeSpec already exists?
Can Harmony/MonoMod dynamically generates necessary TypeSpec for generics?
Code snippet: (runnable with dotnetfiddle.net)
using System;
using HarmonyLib;
namespace PatchTarget {
public abstract class QSingleton<T> where T : QSingleton<T>, new() {
protected static T instance = null; protected QSingleton() { }
public static T Instance { get {
if (instance == null) {
instance = new T();
Console.WriteLine($"{typeof(T).Name}.Instance: impl=QSingleton");
}
return instance;
} }
}
public class SampleA : QSingleton<SampleA> {
public SampleA() { Console.WriteLine("SampleA ctor"); }
}
public class SampleB : QSingleton<SampleB> {
public SampleB() { Console.WriteLine("SampleB ctor"); }
}
}
namespace Patch {
public class TypeHelper<T> where T : PatchTarget.QSingleton<T>, new() {
public static T InstanceHack() {
Console.WriteLine($"{typeof(T).Name}.Instance: impl=InstanceHack");
return null;
}
// For Harmony as Prefix, but attribute does not work.
public static bool InstanceHackPrefix(T __result) {
Console.WriteLine($"{typeof(T).Name}.Instance: impl=InstanceHack");
__result = null;
return false;
}
}
public static class HarmonyPatch {
public static Harmony harmony = new Harmony("Try");
public static void init() {
// Attribute does not work.
// Transpiler does not work because the lack of TypeSpec to setup generic parameters.
var miOriginal = AccessTools.Property(typeof(PatchTarget.QSingleton<PatchTarget.SampleB>), "Instance").GetMethod;
var miHack = AccessTools.Method(typeof(TypeHelper<PatchTarget.SampleB>), "InstanceHackPrefix");
harmony.Patch(miOriginal, prefix: new HarmonyMethod(miHack));
}
}
public static class MonoModPatch {
public static MonoMod.RuntimeDetour.Detour sHook;
public static void init() {
var miOriginal = AccessTools.Property(typeof(PatchTarget.QSingleton<PatchTarget.SampleB>), "Instance").GetMethod;
var miHack = AccessTools.Method(typeof(TypeHelper<PatchTarget.SampleB>), "InstanceHack");
sHook = new MonoMod.RuntimeDetour.Detour(miOriginal, miHack);
}
}
}
class Program {
public static void Main() {
_ = PatchTarget.SampleA.Instance;
// MonoMod works (replaces globally).
// Harmony hooks, but in an expected way (T becomes SampleB, not 1st generic type parameter).
// try { Patch.HarmonyPatch.init(); } catch (Exception e) { Console.WriteLine($"Harmony error: {e.ToString()}"); }
try { Patch.MonoModPatch.init(); } catch (Exception e) { Console.WriteLine($"MonoMod error: {e.ToString()}"); }
_ = PatchTarget.SampleB.Instance;
_ = PatchTarget.SampleA.Instance;
Console.WriteLine($"done");
}
}
MonoMod.RuntimeDetour Output:(Work as intended)
SampleA.Instance: impl=QSingleton
SampleB.Instance: impl=InstanceHack
SampleA.Instance: impl=InstanceHack
Harmony Output:(Broken <T>)
SampleA.Instance: impl=QSingleton
SampleB.Instance: impl=InstanceHack
SampleB.Instance: impl=InstanceHack
I'm essentially trying to upcast an object but I don't know how to deal with the generics. Below is a super-contrived example but it illustrated a situation I'm working with. Perhaps I need an implicit operator but I'm not sure what that would look like in this scenario.
using System;
using System.Collections.Generic;
class MainClass {
public static void Main (string[] args) {
var cats = new Dictionary<string, IAnimal<ICat>>()
{
{ "paws", new Tabby() },
{ "teeth", new MountainLion() }
};
foreach (var cat in cats)
{
cat.Value.talk();
}
}
public interface IAnimal<T> where T : ICat
{
void talk();
}
public interface ICat
{
}
public class HouseCat : ICat
{
}
public class BigCat : ICat
{
}
public class MountainLion : IAnimal<BigCat>
{
public void talk() {
Console.WriteLine("Rawr!");
}
}
public class Tabby : IAnimal<HouseCat>
{
public void talk() {
Console.WriteLine("Meow");
}
}
}
Thanks to #kalten I arrived at this solution:
public interface Animal<out T> where T : Cat
You can see it working here: https://repl.it/#austinrr/FlippantLonelyTab#main.cs
I have attribute class
[AttributeUsage(AttributeTargets.Method)]
public class MethodGetterAttribute : ExportAttribute
{
}
I'm using it in method of several namespaces:
namespace Model.First
{
public class PersonBL
{
[MethodGetter]
public void GetName(Person person)
{
}
}
}
namespace Model.First.Second
{
public class PersonBL
{
[MethodGetter]
public void GetName(Person person)
{
}
}
}
namespace Model.First.Second.Third
{
public class WorkerBL
{
[MethodGetter]
public void GetName(Worker worker)
{
}
}
}
I want to order all methods and run it one by one. To get methods I'm doing this:
Assembly[] assemblies = AppDomain.CurrentDomain.GetAssemblies();
var catalog = new AggregateCatalog();
catalog.Catalogs.Add(new AssemblyCatalog(assemblies.FirstOrDefault(a => a.GetName().Name.Contains("Model"))));
var container = new CompositionContainer(catalog);
var importedMethods = container.GetExports<Action<Worker>>() as IEnumerable<Lazy<Action<Worker>>>;
var result = importedMethods.Select(a => a.Value.Target).ToList();// Here i'm getting only worker's method
But it returns only Worker's method. How can I get all three methods from worker?
Well...
Let's create 4 class libraries
Zero.dll with all classes used in other assemblies
using System;
using System.ComponentModel.Composition;
using System.Diagnostics;
namespace Zero
{
[AttributeUsage(AttributeTargets.Method)]
public class MethodGetterAttribute : ExportAttribute { }
public class Person { }
public class Worker : Person { }
public static class MethodHelper
{
public static string GetMethod()
{
var method = new StackTrace().GetFrame(1).GetMethod();
return $"{method.DeclaringType.FullName} {method}";
}
}
public static class Discovery
{
public static TDelegate[] GetDelegates<TAttribure, TDelegate>()
where TAttribure : Attribute
where TDelegate : Delegate
{
return Directory.GetFiles(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), "*.dll")
.Select(file => { try { return Assembly.LoadFrom(file); } catch { return null; } })
.OfType<Assembly>()
.Append(Assembly.GetEntryAssembly())
.SelectMany(assembly => assembly.GetTypes())
.SelectMany(type => type.GetMethods())
.Where(method => method.GetCustomAttributes(typeof(TAttribure)).Any())
.Select(method => Delegate.CreateDelegate(typeof(TDelegate), null, method, false))
.OfType<TDelegate>()
.ToArray();
}
}
}
Model.First.dll referencing Zero.dll
using System;
using Zero;
namespace Model.First
{
public class PersonBL
{
[MethodGetter]
public void GetName(Person person)
{
Console.WriteLine(MethodHelper.GetMethod());
}
}
}
Model.First.Second.dll referencing Zero.dll
using System;
using Zero;
namespace Model.First.Second
{
public class PersonBL
{
[MethodGetter]
public void GetName(Person person)
{
Console.WriteLine(MethodHelper.GetMethod());
}
[MethodGetter]
public void Incompatible(string s)
{
Console.WriteLine(MethodHelper.GetMethod());
}
}
}
Model.First.Second.Third.dll referencing Zero.dll
using System;
using Zero;
namespace Model.First.Second.Third
{
public class WorkerBL
{
[MethodGetter]
public void GetName(Worker worker)
{
Console.WriteLine(MethodHelper.GetMethod());
}
public void NoAttribute(Worker worker)
{
Console.WriteLine(MethodHelper.GetMethod());
}
}
}
Then let's create console application ConsoleApp.exe referencing Zero.dll, Model.First.dll, Model.First.Second.dll and Model.First.Second.Third.dll
using System;
using Zero;
namespace ConsoleApp
{
class Program
{
static void Main(string[] args)
{
var worker = new Worker();
foreach (var d in Discovery.GetDelegates<MethodGetterAttribute, Action<Worker>>())
d.Invoke(worker);
}
}
public class WorkerBL
{
[MethodGetter]
public void GetName(Worker worker)
{
Console.WriteLine(MethodHelper.GetMethod());
}
}
}
Let's create Junk.txt, put some nonsense like bd%E56#EVwD into it, rename the file to Junk.dll and add it into .exe file directory and then start the application.
Output is:
Model.First.PersonBL Void GetName(Zero.Person)
Model.First.Second.PersonBL Void GetName(Zero.Person)
Model.First.Second.Third.WorkerBL Void GetName(Zero.Worker)
ConsoleApp.WorkerBL Void GetName(Zero.Worker)
As expected. It finds all compatible methods with specified attribute and returns delegates for them.
I'm new to C#, I'm in doubt about how to make this work:
namespace Core {
public class A{
private reandonly string _var;
public A(string var){
_var=var
}
public GetValue() => return _var;
}
}
using System;
namespace Core.Resources {
public static class B{
public static void DoSomething(){
Console.Writeline($"{A.GetValue()}");
}
}
}
public class C{
static void Main(string args[]){
A a = new A("name");
a.Resources.B.DoSomething();
}
}
A is in main folder, B is in Main/Resources folder, together they make a classlib, Program.cs is using this lib. Is there a way to make this work?
If you write a.Resources you are basically trying to retrieve the member Resources of the class A, which is obviously not defined. Since B is a static class defined in the Core.Resources namespace, all you have to do is to change your code as follows:
public class C
{
public static void Main(string args[])
{
A a = new A("A");
Core.Resources.B.DoSomething();
}
}
or, alternatively, if you don't want to reference the namespace every time:
using Core.Resources;
public class C
{
public static void Main(string args[])
{
A a = new A("A");
B.DoSomething();
}
}
Note that if yuu explicitly define a public constructor for A that accepts one or more arguments, the default parameterless constructor is no more available... hence you have to pass a string to the A constructor if you don't want to see an error in your console. Alternatively, you have to rewrite your A class so that it implements a default parameterless compiler, for example:
public class A
{
private reandonly String _var;
public A() : this(String.Empty) { }
public A(String var)
{
_var = var;
}
}
EDIT AS PER OP COMMENTS AND QUESTION CHANGES
public class A
{
private reandonly String _var;
public String Var
{
get { return _var; }
}
public A(String var)
{
_var = var;
}
}
public static class B
{
public static void DoSomething(String text)
{
Console.Writeline(text);
}
}
public class C
{
public static void Main(string args[])
{
A a = new A("name");
B.DoSomething(a.Var);
}
}
I have the following classes
public class A
{
protected static Dictionary<string,Func<BaseClass>> dict = new Dictionary<string,Func<BaseClass>>();
public static void AddGenerator(string type,Func<BaseClass> fncCreateObject)
{
dict.Add(type,fncCreateObject);
}
}
class B : BaseClass
{
static B()
{
A.AddGenerator("b",CreateObject);
}
protected B()
{}
pulic static B CreateObject()
{
return new B();
}
}
NOTE: The above code is simply an example but very closely relates to the what I'm trying to achieve.
Many people would advice using an IoC container such as NInject or Unity but my main reason for this post if to figure out why the above code does not execute as it is expected to.
So, in the above code, I'm expecting class B's static constructor to call on the static method of class A and an entry should be available in the dictionary for the rest of the application life cycle.
However, when I run the code and debug, I found that the dictionary is empty.
Why is the code invoked from class B's static constructor not executing?
From the documentation:
A static constructor is called automatically to initialize the class before the first instance is created or any static members are referenced.
Clearly, at the point in your code where you inspect the dictionary, no instance has yet been created, and no static members have been referenced.
Not exactly a 1:1 translation, of your sample into MEF, but it should give you a good idea what MEF is capable of:
using System;
using System.Collections.Generic;
namespace ConsoleApplication4
{
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var directoryCatalog = new DirectoryCatalog(".");
var compositeCatalog = new AggregateCatalog(assemblyCatalog, directoryCatalog);
var container = new CompositionContainer(compositeCatalog);
var a = A.Instance;
container.SatisfyImportsOnce(a);
a.PrintCatalog();
}
}
public sealed class A
{
private static readonly A instance = new A();
static A() { }
private A() { }
public static A Instance { get { return instance; } }
[ImportMany]
private List<IBType> BTypes;
public void PrintCatalog()
{
foreach (var bType in BTypes)
{
Console.WriteLine(bType.GetType());
}
}
}
[Export(typeof(IBType))]
class B:IBType
{
static B()
{
}
protected B()
{}
public void DoSomething() { }
}
[Export(typeof(IBType))]
class B2:IBType
{
static B2()
{
}
protected B2()
{}
public void DoSomething() { }
}
interface IBType
{
void DoSomething();
}
}
I've also included the safest implementation of a Singleton pattern known to me. MEF will allow you to source many implementations of the same interface which are resolved dynamically at runtime. I used it also with metadata attributes, like version and name.
But if you need it to work with a base abstract class, check out this article.
The same code as above, but with metadata attributes use sample:
using System;
using System.Collections.Generic;
namespace ConsoleApplication4
{
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Linq;
using System.Reflection;
class Program
{
static void Main(string[] args)
{
var assemblyCatalog = new AssemblyCatalog(Assembly.GetExecutingAssembly());
var directoryCatalog = new DirectoryCatalog(".");
var compositeCatalog = new AggregateCatalog(assemblyCatalog, directoryCatalog);
var container = new CompositionContainer(compositeCatalog);
var a = A.Instance;
container.SatisfyImportsOnce(a);
a.PrintCatalog();
a.BTypes.Single(s=>s.Metadata.Name.Equals("Second")).Value.DoSomething();
}
}
public sealed class A
{
private static readonly A instance = new A();
static A() { }
private A() { }
public static A Instance { get { return instance; } }
[ImportMany]
public List<Lazy<IBType,IBTypeMetadata>> BTypes;
public void PrintCatalog()
{
foreach (var bType in BTypes)
{
Console.WriteLine(bType.Value.GetType());
}
}
}
[Export(typeof(IBType))]
[BTypeMetadata("First")]
class B:IBType
{
static B()
{
}
protected B()
{}
public void DoSomething() { }
}
[Export(typeof(IBType))]
[BTypeMetadata("Second")]
class B2 : IBType
{
static B2()
{
}
protected B2()
{}
public void DoSomething()
{
Console.WriteLine("Hello from Second");
}
}
public interface IBType
{
void DoSomething();
}
public interface IBTypeMetadata
{
string Name { get; }
}
[MetadataAttribute]
[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class BTypeMetadataAttribute : ExportAttribute
{
public string Name { get; set; }
public BTypeMetadataAttribute(string name)
: base(typeof(IBTypeMetadata)) { Name = name; }
}
}
IMHO, MEF might help you as long as your plan is to call some public methods from a particular instance of any of the B-types. In your sample, you simply create new instances of a B-type, and I think there is more to it than what your sample shows.
MEF will create catalogs for you from your currently loaded assembly, as well as any number of assemblies from any number of directories. You can even have it dynamically re-composable, meaning, at runtime, you could potentially retrieve a DLL from a server, and have it added to your catalog without shutting down the application.
MEF is also hierarchical, so your B-types can have their own "catalogs". And to wire it all up, all you have to do is to call SatifyImportsOnce passing an instance of class A.