Вам нужно как-то получить доступ к экземпляру ServiceConsoleHandler
через экземпляр RemoteObject
, который виден для клиента.
Для этого вам необходимо учесть две вещи: (1) получить контроль над созданием объекта экземпляра RemoteObject
и сделать его доступным, (2) изменить ServiceConsoleHandler
, чтобы к нему можно было получить удаленный доступ.
(1)
Как бы вы создали RemoteObject
экземпляр в ServiceConsoleHandler
, если вам не нужно учитывать удаленное взаимодействие?
Полагаю, вы бы сделали что-то вроде этого:
class ServiceConsoleHandler
{
…
RemoteObject remoteObject = new RemoteObject();
// now assume that you also already have
// modified the RemoteObject class so it can hold
// a reference to your server:
remoteObject.Server = this;
…
}
Было бы неплохо, если бы вы могли сделать этот объект доступным для клиента. Вы можете сделать это, используя RemotingServices.Marshal вместо RemotingConfiguration.RegisterWellKnownServiceType:
class ServiceConsoleHandler
{
…
TcpServerChannel channel = new TcpServerChannel(9090);
ChannelServices.RegisterChannel(channel, true);
RemoteObject remoteObject = new RemoteObject();
remoteObject.Server = this;
RemotingServices.Marshal(remoteObject, "RemoteObject.rem");
…
}
* * 1 022 (2)
Если вы выполните код прямо сейчас и получите доступ к remoteObject.Server
в клиентском коде, вы получите исключение удаленного взаимодействия, поскольку к классу ServiceConsoleHandler
нельзя получить удаленный доступ. Поэтому вам нужно добавить атрибут [Serializable]
:
[Serializable]
class ServiceConsoleHandler
{ … }
Причина: типы, к которым следует обращаться удаленно, должны быть преобразованы в какое-то специальное передаваемое представление. Таким образом, они могут быть сжаты через порт TCP и переданы по протоколу TCP. Базовые типы данных могут быть упорядочены структурой, поэтому вам не нужно думать о них. Для пользовательских типов вам нужно указать, как это сделать. Один из способов сделать это - создать подкласс от MarshalByRefObject
. Это именно то, что вы уже сделали с RemoteObject
. Другой способ - пометить ваши пользовательские классы как [Serializable]
, как показано выше.
Вот и все. Теперь вы сможете получить доступ к полю сервера в коде клиента. Обратите внимание, что вам не нужен существующий код для активации объекта:
TcpClientChannel channel = new TcpClientChannel();
ChannelServices.RegisterChannel(channel, true);
RemoteObject remoteObject = (RemoteObject)Activator.GetObject(
typeof(RemoteObject), "tcp://localhost:9090/RemoteObject.rem");
Console.WriteLine(remoteObject.Server.SomethingMore);
Для меня .NET Remoting полон забавных сюрпризов и бессонных ночей. Чтобы противостоять этому, ознакомьтесь с концепциями удаленного взаимодействия (которые, с моей точки зрения, плохо документированы). Окунитесь в концепцию сериализации (MarshalByRefObject
против [Serializable]
). Если вы хотите сделать из него производственный код, подумайте об очень хороших способах обработки исключений удаленного взаимодействия. Также рассмотрим многопоточность. Может быть несколько клиентов, использующих этот удаленный объект одновременно.
Веселись!