【问题标题】:Object instance valid only for the current method对象实例仅对当前方法有效
【发布时间】:2015-04-26 20:28:04
【问题描述】:

是否可以创建一个对象来注册当前线程是否离开创建它的方法,或者在实例上的方法被调用时检查是否发生了这种情况?

ScopeValid obj;

void Method1()
{
    obj = new ScopeValid();
    obj.Something();
}

void Method2()
{
    Method1();
    obj.Something(); //Exception
}

这种技术可以实现吗?我想开发一个类似于TypedReferenceArgIterator 的机制,它不能“逃避”当前的方法。这些类型是由编译器专门处理的,所以我不能完全模仿这种行为,但我希望至少可以创建一个具有相同结果的类似规则 - 如果对象已经转义了它所在的方法,则不允许访问该对象已创建。

请注意,我不能使用StackFrame 和比较方法,因为对象可能会转义并返回到相同的方法。

【问题讨论】:

  • 您的用例是什么?在您的示例中,似乎objMethod1() 的本地对象会更好地表达意图
  • 使用ScopeValid的代码是用户创建的。即使我说不能这样做,一些讨厌的程序员可能会破坏它,所以我想知道它是否可以强制执行。
  • 好的,但是使其他方法可以引用和尝试使用的方法范围成员的用例是什么?
  • @jdphenix 我不明白。 ArgIterator 不能在方法外部使用,因为它访问传递给方法的参数,这些参数只存在于方法内部。 ScopeValid 会做类似的事情,所以类似的强制措施会派上用场。
  • 你为什么要实现这个? - 我想这已经很清楚了。

标签: c# .net


【解决方案1】:

根据调用源更改方法行为是一个糟糕的设计选择。

使用这种方法需要考虑的一些示例问题包括:

  • 可测试性 - 你会如何测试这样的方法?
  • 重构调用代码 - 如果你的代码的用户只是在你的错误消息中结束运行,说你不能用与创建它不同的方法来做到这一点怎么办? “好吧,好吧!我会用同样的方法做我的坏事,程序员说。”

如果您的代码的用户破坏了它,这是他们的错,让它破坏。最好只用以下内容记录您的代码:

IInvalidatable - 完成此操作后,应使用Invalidate() 使实现此成员的类型无效。

忽略这个 几乎看起来 正在重新发明 IDisposibleusing { } 块(具有语言支持)的明显点,如果您的代码的用户没有正确使用它,这不是你真正关心的问题。


这在技术上很可能通过 AOP(我认为 PostSharp 在这里),但它仍然取决于用户正确使用您的代码 - 他们必须在构建过程中拥有它,并且如果它们不能运行则无法运行'仅仅因为你试图让他们轻松而使用工具是邪恶的。

另一点 - 如果您只是试图创建一个不能在一个方法之外使用的对象,并且在该方法之外的任何尝试操作都会失败,只需在方法内将其声明为本地。

相关:How to find out which assembly handled the request

【讨论】:

    【解决方案2】:

    多年后,这个功能似乎终于被添加到 C# 7.2:ref struct

    另一个相关的语言特性是能够声明必须堆栈分配的值类型。换句话说,这些类型永远不能作为另一个类的成员在堆上创建。此功能的主要动机是 Span 和相关结构。 Span 可以包含一个托管指针作为其成员之一,另一个是 span 的长度。它实际上的实现有点不同,因为 C# 不支持指向不安全上下文之外的托管内存的指针。任何更改指针和长度的写入都不是原子的。这意味着 Span 将受到超出范围错误或其他类型安全违规的影响,如果它不限于单个堆栈帧。此外,将托管指针放在 GC 堆上通常会在 JIT 时崩溃。

    这可以防止代码将值移动到堆中,这部分解决了我原来的问题。不过,我不确定返回 ref struct 是如何受到限制的。

    【讨论】:

    • 观察:如果您想“捕获”ref struct,您仍然可以通过将其捕获在暂停的线程中来实现。我使用了类似的技术来无限期地固定引用而不使用 GCHandle,方法是在 CIL 中发出 pinned 然后暂停线程。
    猜你喜欢
    • 2012-03-19
    • 2016-09-19
    • 2010-11-23
    • 1970-01-01
    • 2014-12-22
    相关资源
    最近更新 更多