Как обойти проблему "ссылки на объект"при передаче сообщений субъекту?


Некоторое время назад я собрал простой класс с именем Actor, который был моей реализацией модели актора . С тех пор я использовал его с большим успехом (за вычетом некоторых раздражающих обходных путей из-за отсутствия дискриминированного типа Союза.). Я остаюсь с проблемой, которую я не уверен, как решить, не делая класс неуклюжим и медленным.

Когда кто-то определяет сообщение, он, конечно, имеет право включить ссылку на объект, который сам вызывающий объект определяет. можете манипулировать. Даже зная, что я буду практически единственным человеком, использующим этот класс, это все еще беспокоит меня.

Хорошим примером того, как это можно обойти, являетсяWeb Workers , реализованный в Firefox. При передаче объекта рабочему он сериализуется в JSON.

Есть идеи?

public abstract class Actor<T, U> : IDisposable
{
    private const int AsyncChannelPoolSize = 20;

    private volatile bool _disposed;
    private readonly Thread _actorThread;
    private readonly AsyncReplyChannel<T, U> _messageChannel;
    private readonly Lazy<ObjectPool<AsyncChannel<U>>> _asyncChannelPool;


    public event EventHandler<ExceptionEventArgs> Exception;


    protected Actor()
    {
        _messageChannel = new AsyncReplyChannel<T, U>();
        _asyncChannelPool = new Lazy<ObjectPool<AsyncChannel<U>>>(() => new ObjectPool<AsyncChannel<U>>(AsyncChannelPoolSize));
        _actorThread = new Thread(ProcessMessages);
        _actorThread.IsBackground = true;
        _actorThread.Start();
    }


    public U PostWithReply(T value)
    {
        ThrowIfDisposed();

        var replyChannel = default(AsyncChannel<U>);
        var replyPackage = default(AsyncReplyPackage<T, U>);
        var replyMessage = default(U);

        try
        {
            replyChannel = _asyncChannelPool.Value.Get();
            replyPackage = new AsyncReplyPackage<T, U>(value, replyChannel);
            _messageChannel.Send(replyPackage);
            replyMessage = replyChannel.Receive();
        }
        finally
        {
            _asyncChannelPool.Value.Put(replyChannel);
        }

        return replyMessage;
    }

    public void PostWithAsyncReply(T value, IAsyncChannel<U> replyChannel)
    {
        ThrowIfDisposed();
        _messageChannel.Send(new AsyncReplyPackage<T, U>(value, replyChannel));
    }

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

    protected abstract void ProcessMessage(AsyncReplyPackage<T, U> package);

    protected virtual void OnException(Exception ex)
    {
        var exceptionEvent = Exception;
        if (exceptionEvent != null)
        {
            exceptionEvent(this, new ExceptionEventArgs(ex));
        }
    }

    protected virtual void Dispose(bool disposing)
    {
        _disposed = true;
        _messageChannel.Dispose();
        if (_asyncChannelPool.IsValueCreated)
        {
            _asyncChannelPool.Value.Dispose();
        }
    }

    private void ProcessMessages()
    {
        var package = default(AsyncReplyPackage<T, U>);
        while (_messageChannel.TryReceive(out package) && !_disposed)
        {
            try
            {
                ProcessMessage(package);
            }
            catch (Exception ex)
            {
                OnException(ex);
            }
        }
    }

    private void ThrowIfDisposed()
    {
        if (_disposed)
        {
            throw new ObjectDisposedException(GetType().FullName);
        }
    }
}
2 2

2 ответа:

Не то чтобы это хорошее решение, но вы можете ограничить T быть ICloneable и клонировать объект, когда вы его получите. Это более или менее идеологический эквивалент подхода" сериализовать в JSON", который вы описали. Излишне говорить, что это было бы неуклюже и медленно.

На самом деле, вы должны просто помнить, чтобы сообщения оставались неизменными. C# - это не тот язык, который может обеспечить это очень хорошо для вас.

Я думаю, что у вас здесь нет проблем-если на вашем _messageChannel происходит какая - либо сериализация, то ваш вызываемый объект не получит ссылку на исходный объект, они получат копию исходного объекта.

Если важно, чтобы вы получили эту" ссылку", возвращенную вам по завершении вызова, то вы можете подумать об использовании идентификатора вместо того, чтобы просто полагаться на ссылку на класс, а затем использовать этот идентификатор для поиска исходного объекта.