【问题标题】:Passing a stack variable as an event argument将堆栈变量作为事件参数传递
【发布时间】:2020-06-22 16:43:01
【问题描述】:

我有一个事件代理,它公开了一个EventHandler<T>,它允许观察者检查事件参数,并在需要时对其进行修改。虽然这可以正常工作,但我希望确保 T 仅存在于堆栈中,此外,任何组件都不能引用 T,从而延长其生命周期。

public class Game // mediator pattern
{
  public event EventHandler<Query> Queries; // effectively a chain

  public void PerformQuery(object sender, Query q)
  {
    Queries?.Invoke(sender, q);
  }
}

遗憾的是,ref struct 不能用作通用参数:

ref struct Query {} // EventHandler<Query> not allowed

同样,我不能在EventHandlerTEventArgs 中灌输任何“使用结构,通过引用传递”机制。

现在,在 C# 中,我们可以决定变量是否存在于堆上的堆栈中,例如使用stackalloc 之类的,所以我想,我所追求的只是在事件中获得与ref struct 等效的东西的一种方式。

【问题讨论】:

标签: c#


【解决方案1】:

虽然stackalloc 以非常复杂的方式应用/包装可能(可能)给您一些variables live on the stack on the heap 的相似之处,但它不会是stackallocintended 的样子。

所以我宁愿建议专注于no component is able to take a reference to T, thereby extending its lifetime 部分。

我们需要得到它

  1. 包装类(可能,但与相应接口无关)
  2. 实现IDisposable
  3. 并将实际的T 存储为WeakReference

会是这样的

public interface ITakeNoRefClass
{
    void Change(string value);
}

public class TakeNoRefClass : ITakeNoRefClass
{
    ...
}

public class TakeNoRefClassWrapper : ITakeNoRefClass, IDisposable
{
    private bool _isDisposed;
    private readonly WeakReference<TakeNoRefClass> _takeNoRefWeakRef;

    public TakeNoRefClassWrapper(WeakReference<TakeNoRefClass> takeNoRefWeakRef)
    {
        _takeNoRefWeakRef = takeNoRefWeakRef;
    }

    public void Change(string value)
    {
        Execute(o => o.Change(value));
    }

    private void Execute(Action<ITakeNoRefClass> action)
    {
        if (_disposed)
        {
            throw new ObjectDisposedException("You should not have taken this ref");
        }
        var target = _takeNoRefWeakRef.Target;
        if (target == null)
        {
            throw new ObjectDisposedException("You should not have taken this ref");
        }
        action(target);
    }

    public void Dispose()
    {
        _isDisposed = true;
    }
}

它应该像这样使用

public void CreateObjectAndRaiseEvents()
{
    var target = new TakeNoRefClass();
    // Passing it into a separate method to ensure that it won't be GC'ed before executing all event handlers.
    RaiseEvents(target);
}

private void RaiseEvent(TakeNoRefClass target)
{
    using (var wrapper = new TakeNoRefClassWrapper(new WeakReference<TakeNoRefClass>(target))
    {
        _event?.Invoke(wrapper);
    }
}

【讨论】:

    猜你喜欢
    • 2014-10-17
    • 1970-01-01
    • 2020-09-18
    • 2010-10-09
    • 2014-11-14
    • 2020-09-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多