可以在 C# 3 或 4 中做到这一点吗?也许有一些反思?
class Magic
{
[RunBeforeAll]
public void BaseMethod()
{
}
//runs BaseMethod before being executed
public void Method1()
{
}
//runs BaseMethod before being executed
public void Method2()
{
}
}
编辑
对此有一个替代解决方案,将
Magic
设为单例并将代码放在静态实例的 getter 上。这就是我所做的:
public class Magic
{
private static Magic magic = new Magic();
public static Magic Instance
{
get
{
magic.BaseMethod();
return magic;
}
}
public void BaseMethod()
{
}
//runs BaseMethod before being executed
public void Method1()
{
}
//runs BaseMethod before being executed
public void Method2()
{
}
}
您无法在 C# 中自动执行此操作 - 您可能应该查看 AOP,例如与PostSharp。
对此有一个替代解决方案,使 Magic 成为单例并将代码放在静态实例的 getter 上。我就是这么做的。
public class Magic{
private static Magic magic;
public static Magic Instance{
get
{
BaseMethod();
return magic;
}
}
public void BaseMethod(){
}
//runs BaseMethod before being executed
public void Method1(){
}
//runs BaseMethod before being executed
public void Method2(){
}
}
我知道它不会直接回答问题。但使用装饰器模式来解决这个问题是一个好方法,可以使您的实现保持干净。
创建界面
public interface IMagic
{
public void Method1()
{
}
public void Method2()
{
}
}
创建实施
public class Magic : IMagic
{
public void Method1()
{
}
public void Method2()
{
}
}
创建装饰器
public class MagicDecorator : IMagic
{
private IMagic _magic;
public MagicDecorator(IMagic magic)
{
_magic = magic;
}
private void BaseMethod()
{
// do something important
}
public void Method1()
{
BaseMethod();
_magic.Method1();
}
public void Method2()
{
BaseMethod();
_magic.Method2();
}
}
用法
var magic = new MagicDecorator(new Magic());
magic.Method1();
magic.Method2();
您想要的可以通过 AOP 完成 - .NET C# AOP 框架的一些链接:
如果您使用依赖注入框架来生成实例,那么您可以使用拦截器进行方法调用。
public class CallLogger : IInterceptor
{
TextWriter _output;
public CallLogger(TextWriter output)
{
_output = output;
}
public void Intercept(IInvocation invocation)
{
_output.Write("Calling method {0} with parameters {1}... ",
invocation.Method.Name,
string.Join(", ", invocation.Arguments.Select(a => (a ?? "").ToString()).ToArray()));
invocation.Proceed();
_output.WriteLine("Done: result was {0}.", invocation.ReturnValue);
}
}
您可以查看 autofac 的文档和示例以获取更多信息。 https://autofaccn.readthedocs.io/en/latest/advanced/interceptors.html
只是为了弄清楚为什么以下实现不起作用:
public class Magic{
private static Magic magic = new Magic();
public static Magic Instance{
get
{
magic.BaseMethod();
return magic;
}
}
public void BaseMethod(){
}
//runs BaseMethod before being executed
public void Method1(){
}
//runs BaseMethod before being executed
public void Method2(){
}
}
如果你只想持有一个
Magic
对象,该方法将被随机调用:
Magic m = Magic.Instance; //this will trigger unwanted call on BaseMethod
如果有人想调用
BaseMethod
,它会被调用两次:
Magic.Instance.BaseMethod(); //two calls of the BaseMethod
当然有一个解决方法,使用 get 返回不需要的对象:
var unused = Magic.Instance;
仅总结一下:这在 C# 中是不可能的(至少目前)。
像这样:
public class Magic{
static Magic()
{
BaseMethod();
}
public void BaseMethod(){
}
//runs BaseMethod before being executed
public void Method1(){
}
//runs BaseMethod before being executed
public void Method2(){
}
}
这是微软文档:
https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/classes-and-structs/static-constructors
https://github.com/Fody/Fody。许可模式基于自愿贡献,这使其成为比 PostSharp 更好的选择,后者对我来说有点贵。
[module: Interceptor]
namespace GenericLogging
{
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Constructor | AttributeTargets.Assembly | AttributeTargets.Module)]
public class InterceptorAttribute : Attribute, IMethodDecorator
{
// instance, method and args can be captured here and stored in attribute instance fields
// for future usage in OnEntry/OnExit/OnException
public void Init(object instance, MethodBase method, object[] args)
{
Console.WriteLine(string.Format("Init: {0} [{1}]", method.DeclaringType.FullName + "." + method.Name, args.Length));
}
public void OnEntry()
{
Console.WriteLine("OnEntry");
}
public void OnExit()
{
Console.WriteLine("OnExit");
}
public void OnException(Exception exception)
{
Console.WriteLine(string.Format("OnException: {0}: {1}", exception.GetType(), exception.Message));
}
}
public class Sample
{
[Interceptor]
public void Method(int test)
{
Console.WriteLine("Your Code");
}
}
}
[TestMethod]
public void TestMethod2()
{
Sample t = new Sample();
t.Method(1);
}
using System;
namespace ConsoleApp1
{
class Class1
{
public enum MethodToCall
{
Method2,
Method3
}
public delegate void MyDelegate(int number = 0, bool doThis = false, double longitude = 32.11);
public static void DoThisFirst(int number, bool doThis, double longitude)
{
Console.WriteLine("DoThisFirst has been called.");
}
public static void DoSomethingElse(int number, bool doThis, double longitude)
{
Console.WriteLine("DoSomethingElse has been called.");
}
public static void DoAnotherThing(int number, bool doThis, double longitude)
{
Console.WriteLine("DoAnotherThing has been called.");
}
public static void Main()
{
void Action(MethodToCall methodToCall)
{
MyDelegate myDel;
myDel = new MyDelegate(DoThisFirst);
switch (methodToCall)
{
case MethodToCall.Method2:
myDel += DoSomethingElse;
break;
case MethodToCall.Method3:
myDel += DoAnotherThing;
break;
}
myDel.Invoke();
}
Action(MethodToCall.Method3);
Console.ReadKey();
}
}
}