2014-11-19 1 views
0

나는 가능한지 확실하지 않은 무언가를 얻으려고하고 있으며 나는 조금 붙어있다.C#의 공분산 및 반 차분 사용법

public class Client 
{ 

} 
public class Server<ClientTemplate> 
    where ClientTemplate : Client 
{ 
    public virtual void ClientConnected(ClientTemplate client){} 
    public virtual void ClientDisconnected(ClientTemplate client){} 
    public virtual void MessageReceived(ClientTemplate client, Message message){} 
    public virtual void SendMessage(ClientTemplate client, Message message){} 
} 

이 클래스는 나중에 같은 다른 어셈블리에 확장됩니다 :

public class LobbyClient : Client 
{ 
    string username; 
    string passwordHash; 
} 
public class LobbyServer : Server<LobbyClient> 
{ 
    public override void ClientConnected(LobbyClient client) 
    { 
     Console.WriteLine("Client connected"); 
    } 
} 

나는 동적으로로드 최초의 조립에서

나는 클라이언트와 서버라는 몇 가지 기본 유형은 다음과 같이 정의되어 내 기본 클래스가 파생 된 두 번째 클래스입니다.

Server<Client> server = Activator.CreateInstance(serverTypeInfo); 

를했지만 운이없이 변환이 잘못 같이

나는 다음이 작업을 수행하기 위해 노력하고있어.

나는 나중에 코드에서 이런 일을하고 싶지 : 나는 더 기본 인터페이스 IClient 및 IServer을 시도

Client client = Activator.CreateInstance(clientType) as Client; 
server.ClientConnected(client); 

및 클라이언트와 서버들로부터했지만 같이 템플릿 인수를 설정을 도출 작동하지.

여기 내 목표를 달성 할 수있는 방법이 있습니까?

Player.IO (현재 야후 게임 네트워크)가이 작업을 관리하고 있지만 컴파일 된 어셈블리를보고 코드가 어떻게 작동하는지 알 수 없습니다.

https://gamesnet.yahoo.net/documentation/services/multiplayer/serverside

편집 :

IServer<IClient> server = Activator.CreateInstance(serverTypeInfo); 

당신에게

감사 : 코드의

public interface IClient 
{ 

} 

public class Client : IClient 
{ 

} 

interface IServer<in ClientTemplate> where ClientTemplate : IClient 
{ 
    void ClientConnected(ClientTemplate client); 
    void ClientDisconnected(ClientTemplate client); 
    void MessageReceived(ClientTemplate client, Message message); 
    void SendMessage(ClientTemplate client, Message message); 
} 

public class Server<ClientTemplate> : IServer<ClientTemplate> 
    where ClientTemplate : IClient 
{ 
    public virtual void ClientConnected(ClientTemplate client){} 
    public virtual void ClientDisconnected(ClientTemplate client){} 
    public virtual void MessageReceived(ClientTemplate client, Message message){} 
    public virtual void SendMessage(ClientTemplate client, Message message){} 
} 

그리고 이상을 : 여기

내가 시도 인터페이스 버전입니다
+0

'IServer 여기서 TClient : IClient'를 사용하셨습니까? 시도한 인터페이스 코드를 게시하십시오. –

+0

나는 그것을 시도했다. 나는 인터페이스를 사용해 보았을 때 코드가 보이는 방식을 정확하게 게시했다. – Sanctus2099

+0

이것을 달성하기 위해'DoubleDispatch'를 사용할 수 있습니다 ...하지만 문제는 ...'Client'와'Server'는 좋은 기본 클래스가 아닙니다. 그것들은 추상적이지만, Liskov와 호환되지 않습니다. 상속의 다른 코드 재사용 개념 (예 : AOP 또는 구성)을 사용해보십시오. – Aron

답변

1

구현에 입력으로 덜 파생 된 매개 변수를 제공하려고 시도 할 때이 경우에는 공분산 (co-contra-variance)이 작동하지 않습니다. 또한 cont-variance는 인터페이스를 통해 작동하며, 형식 매개 변수는 in 또는 out 키워드로 선언되었습니다. 예를 들어

(션 여기 유사 유형)

IServer<Client> server = new Server<LobbyClient>(); 
Client client = new GameClient(); 
server.ClientConnected(client); 

가 입력 LobbyClient 인스턴스로 기대되므로 Server<LobbyClient>하기, IServer<Client>로 전송할 수 없으며 인터페이스 잠재적 임의Client 형태로 전달할 수 .

내가이 문제를 해결하기 위해 생각할 수있는 두 가지 방법이 있지만 둘 다 유형 시스템을 파괴하는 것이 포함됩니다. 따라서 사용중인 유형이 올바른지 확인해야합니다. 그렇지 않으면 런타임 예외가 발생합니다.

첫 번째 방법은 리플렉션을 통해 서버 메소드를 호출하는 것입니다. 그러나 이것은 매우 느리고 장황 할 수 있습니다.

두 번째 방법은 Server 클래스의 비 제네릭 인터페이스를 작성하고 Sever 클래스가이를 명시 적으로 구현하도록하는 한편 각 메소드를 해당 일반 구현에 위임하는 것입니다. 직접 액세스 할 때

public interface IServer 
{ 
    void ClientConnected(Client client){} 
    void ClientDisconnected(Client client){} 
    void MessageReceived(Client client, Message message){} 
    void SendMessage(Client client, Message message){} 
} 

public class Server<ClientTemplate> : IServer 
    where ClientTemplate : Client 
{ 
    void IServer.ClientConnected(Client client) 
    { 
     ClientConnected((ClientTemplate)client); 
    } 

    void IServer.ClientDisconnected(Client client) 
    { 
     ClientDisconnected((ClientTemplate)client); 
    } 

    void IServer.MessageReceived(Client client, Message message) 
    { 
     MessageReceived((ClientTemplate)client, message); 
    } 

    void IServer.SendMessage(Client client, Message message) 
    { 
     SendMessage((ClientTemplate)client, message); 
    } 

    public virtual void ClientConnected(ClientTemplate client){} 
    public virtual void ClientDisconnected(ClientTemplate client){} 
    public virtual void MessageReceived(ClientTemplate client, Message message){} 
    public virtual void SendMessage(ClientTemplate client, Message message){} 
} 

지금, 비 일반적인 방법은 일반적으로 서버 구현에 표시되지 않습니다,하지만 당신은 IServer 인터페이스 인스턴스를 할당하고 그 제네릭이 아닌 메서드를 호출 할 수 있습니다. 유형이 올바르게 일치하지 않으면 런타임 예외가 발생합니다. 당신은 서버 [클라이언트]에 LobbyServer 변환
으로 만약 내가 그것을 할 수있을 것입니다 생각하지 않습니다

IServer server = Activator.CreateInstance(serverTypeInfo) as IServer;  
Client client = Activator.CreateInstance(clientType) as Client; 
server.ClientConnected(client); // works 
1

은 잠재적 인 런타임 오류가 될 것이다.

Server<Client> server = new LobbyServer(); 
server.ClientConnected(new Client()); // how can a LobbyServer handle this? 

정말 답을주지 않았습니다. 미안합니다. 당신이 어떤 Client에 연결을 처리 할 수 ​​LobbyServer 같은 Server 또는 IServer의 전문 구현을 원하는 경우

0

, 당신은 적절하게 지정해야합니다. 그러면 인스턴스를 일반 Server<Client> 또는 IServer<Client>으로 처리 할 수 ​​있습니다.

LobbyServer 따라서 선언 된

,

public class LobbyServer : Server<Client> 
{ 
    public override void ClientConnected(Client client) 
    { 
     // ... 
    } 
} 

다음

var instance = (Server<Client>)Activator.CreateInstance(typeof(LobbyServer)); 

InvalidCastException없이 실행된다.

LobbyServer하는 경우는 다음이 Server<Client>하지 않고 그 형식으로 캐스팅 할 수 없습니다 LobbyClient처럼 더 특수한 유형의 다음 Client로 정의된다.


대답은 당신의 인터페이스가되는 방법, 입력의 안전을 유지하기 위해 contravariant 할 TClient 요구를 선언 당신이 발견 한대로, TClient 공변를 확인하는 것입니다 만. 이런 식으로 극복 할 수 있습니다. 여기

interface IServer<out TClient> where TClient : IClient 
{ 
    void ClientConnected(
      Action<TClient, IServer<TClient>> connectionAction); 
    void ClientDisconnected(
      Action<TClient, IServer<TClient>> disconnectionAction); 
    void MessageReceived(
      Action<TClient, IServer<TClient>, Message> recievedAction); 
    void SendMessage(
      Action<TClient, IServer<TClient>, Message> sendAction); 
} 

Action delagate가 분산 유지 안전 입력의 방향을 역전.

이제 LobbyServer는이 같은

var instance (IServer<IClient>)Activator.CreateInstance(typeof(LobbyServer)); 

public class LobbyServer : Server<LobbyClient> 
{ 
} 

를 선언 할 수있는 것은 완벽하게 유효합니다.

그러나 작업의 구현은 형식을 알 때까지 위임됩니다.