C# 中拦截对属性 get 方法的调用

发布于 2024-08-30 08:44:36 字数 240 浏览 5 评论 0原文

假设我们有这个类:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

现在,在 C# 中是否可以拦截对属性 get 方法的调用,运行其他方法并返回该方法的结果而不是属性值?我希望能够在幕后做一些额外的逻辑。 缺点是此类无法更改(在 C# 级别)。也许一些IL?

Let's assume that we have this class:

public class Person
{
    public int Id { get; set; }
    public string Name { get; set; }
}

Now, is it possible in C# to intercept call to property get method, run some other method and return result of that method instead of property value? I'd like to be able to do some additional logic behind the scene.
The downside is that this class can't be changed (on C# level). Maybe some IL ?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

末蓝 2024-09-06 08:44:36

查看 PostSharp: http://www.sharpcrafters.com/

这正是您正在寻找的东西,还有更多。

PostSharp 实现了 .NET 的面向方面编程。它是一种商业产品。非常酷。

请注意,此问题的任何解决方案都会涉及一些运行时开销(一些内存、一些额外的函数调用时间。)没有什么是绝对免费的。

Look into PostSharp: http://www.sharpcrafters.com/

This implements exactly what you're looking for, and a whole lot more.

PostSharp implements Aspect Oriented Programming for .NET. Its a commercial product. Very cool.

Just be aware that any solution to this problem will involve some runtime overhead (some memory, some extra time to make function calls.) Nothing is absolutely free.

小伙你站住 2024-09-06 08:44:36

如果您的课程无法更改,您可以考虑使用 PostSharp - 它允许您编写拦截或替换对方法和属性的调用的代码。

如果您可以更改代码,那么只需避免使用自动属性,并显式实现 get 和 set 操作即可。然后你就可以做任何你需要做的事情了。或者,您可以将属性定义为 virtual 并派生另一个类来更改 getter 和/或 setter 的行为:

public class Person 
{ 
  public virtual int Id { get; set; } 
  public virtual string Name { get; set; } 
} 

public class PersonDerived : Person
{
  public override int Id { get { ... } set { ... } }
  public override string Name { get { ... } set { ... } }
}

如果您仅需要它进行单元测试,您可能需要研究模拟工具,例如MoqTypeMock

If your class can't be changed, you can look into using tools like PostSharp - it allows you to weave in code that intercepts or replaces calls to methods and properties.

If you can change the code, then just avoid using an automatic property, and implement the get and set operations explicitly. Then you can do whatever you need. Alternatively, you can define the property as virtual and derive another class to change th behavior of the getter and/or setter:

public class Person 
{ 
  public virtual int Id { get; set; } 
  public virtual string Name { get; set; } 
} 

public class PersonDerived : Person
{
  public override int Id { get { ... } set { ... } }
  public override string Name { get { ... } set { ... } }
}

If you need this just for unit testing, you may want to look into mocking tools like Moq or TypeMock.

山色无中 2024-09-06 08:44:36

.NET SDK 已经具备您所需要的功能。如果您小心的话,您可以通过反汇编/重组(ildasm.exe 和 ilasm.exe)来往返 CLR 上的大多数内容。确保启动“Visual Studio 命令提示符”,以便这些工具位于您的路径中。

1) ildasm person.exe

2) 文件->转储(person.il)

3) 编辑修改get_Name()方法:
重要提示:在 IL 级别修改方法时,请始终重新计算 .maxstack,因为 Visual Studio .NET 编译器将为大多数方法准确设置它,如果添加任何代码,请提高 .maxstack 来处理该方法的运行时堆栈上的最大值数,如果不是,您将得到一个无效的程序。

  .method public hidebysig specialname instance string get_Name() cil managed
  {
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       11 (0xb)
    .maxstack  2
    .locals init (string V_0)
    IL_0000:  ldarg.0
    IL_0001:  ldfld      string Person::'<Name>k__BackingField'
    IL_0006:  stloc.0
    IL_0009:  ldloc.0
        IL_000a:  ldstr "IL code inserted here"
        call    void [mscorlib]System.Console::WriteLine(string)
    IL_001a:  ret
 } // end of method Person::get_Name

4)重新组装:ilasm person.il

The .NET SDK has what you need already. You can round-trip most anything on the CLR by disassembly/reassembly (ildasm.exe and ilasm.exe) if you are careful. Make sure to start a "Visual Studio Command Prompt" so these tools are in your PATH.

1) ildasm person.exe

2) File -> Dump (person.il)

3) Edit and modify the get_Name() method:
IMPORTANT: When modifying a method at the IL level, always recalculate .maxstack, since the Visual Studio .NET compilers will have set it exactly for most methods, if you add any code, raise .maxstack to handle the maximum # of values on the runtime stack for that method, if not you'll get an invalid program.

  .method public hidebysig specialname instance string get_Name() cil managed
  {
    .custom instance void [mscorlib]System.Runtime.CompilerServices.CompilerGeneratedAttribute::.ctor() = ( 01 00 00 00 ) 
    // Code size       11 (0xb)
    .maxstack  2
    .locals init (string V_0)
    IL_0000:  ldarg.0
    IL_0001:  ldfld      string Person::'<Name>k__BackingField'
    IL_0006:  stloc.0
    IL_0009:  ldloc.0
        IL_000a:  ldstr "IL code inserted here"
        call    void [mscorlib]System.Console::WriteLine(string)
    IL_001a:  ret
 } // end of method Person::get_Name

4) Re-assemble : ilasm person.il

递刀给你 2024-09-06 08:44:36

扩展课程并做

public class MyPerson : Person
{
    public new int Id 
    { 
        get
        {
            foo();
            return base.Id;
        }
}

Extend the class and do

public class MyPerson : Person
{
    public new int Id 
    { 
        get
        {
            foo();
            return base.Id;
        }
}
嘦怹 2024-09-06 08:44:36

不具有自动属性,不。

但你可以这样做:

public class Person
{
    private int _id;

    public int Id
    {
        get
        {
            // Do some logic or call a method here
            return _id;
        }
        set
        {
            if(value <= 0)
                throw new CustomInvalidValueException();

            _id = value;
        }
    }
}

Not with auto-properties, no.

But you could do:

public class Person
{
    private int _id;

    public int Id
    {
        get
        {
            // Do some logic or call a method here
            return _id;
        }
        set
        {
            if(value <= 0)
                throw new CustomInvalidValueException();

            _id = value;
        }
    }
}
败给现实 2024-09-06 08:44:36
public Class Person
{
    private int _id;
    public int ID
    {
      get{ MyMethod(); return _id; }
      set{ _id = value; }
    }
   // etc. etc. .....

 }
public Class Person
{
    private int _id;
    public int ID
    {
      get{ MyMethod(); return _id; }
      set{ _id = value; }
    }
   // etc. etc. .....

 }
此岸叶落 2024-09-06 08:44:36
private bool _pp;
public bool Propery
{
  get { // your code}
  set { _pp = value; }
}
private bool _pp;
public bool Propery
{
  get { // your code}
  set { _pp = value; }
}
十六岁半 2024-09-06 08:44:36

我认为您正在寻找面向方面的框架

I think you're looking for an Aspect Oriented Framework.

避讳 2024-09-06 08:44:36

如果您无法更改类本身,您可以考虑使用扩展方法来获取名称。这将改变您访问数据的方式,因此它可能也不是一个选择。如果您有兴趣,这就是您的做法:

public static class PersonExtensions
{
    public static string GetName(this Person p)
    {
        foo();
        return p.Name;
    }
}

然后您可以通过以下方式访问该名称:

Person myPerson = new Person();
string name = myPerson.GetName();  // Also calls foo() for you extra processing

希望这会有所帮助。

If you can't change the class itself, you might consider using an extension method to get the name. This will change the way you access the data, so it might not be an option either. This is how you'd do it, if you're interested:

public static class PersonExtensions
{
    public static string GetName(this Person p)
    {
        foo();
        return p.Name;
    }
}

You would then access the name this way:

Person myPerson = new Person();
string name = myPerson.GetName();  // Also calls foo() for you extra processing

Hope this helps.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文