Tengo algunos problemas graves de diseño debido a problemas con los genéricos. Tal vez alguien tiene algunas sugerencias.Conversión de tipo genérico
EDITAR: Entonces, sé que esto no suele hacerse, pero he cambiado completamente mi código de ejemplo, porque me he dado cuenta de que el pseudocódigo original realmente no explicaba mi problema. El siguiente código se parece mucho más al ejemplo real al que me refiero. Espero que mi problema sea más claro a partir de ahí. Me disculpo por adelantado porque es un poco largo, pero según mi experiencia, los problemas con los genéricos generalmente aparecen cuando intentas construir una estructura más compleja. Por lo tanto:
class Program
{
static void Main(string[] args)
{
IConnector<IService> connector = ConnectorBuilderFactory.NewBuilder<IService>("someEndpoint").MakeReliable().GetConnector();
connector.Connect();
}
}
public interface IService : IConnectionMaintainable
{
void DoSomething();
}
public interface IConnectionMaintainable
{
DateTime GetServerTime();
}
public interface IConnector<T>
{
T Channel { get; }
void Connect();
void Disconnect();
}
public interface IConnectorBuilder<T>
{
IConnector<T> GetConnector();
IConnectorBuilder<T> MakeReliable();
// ...more connector-configuration methods
}
public class ChannelWatchDog<T> where T : IConnectionMaintainable
{
private IConnector<T> connector;
public ChannelWatchDog(IConnector<T> connector /*various other parameters*/)
{
this.connector = connector;
}
// ...methods that use connector's Connect, Disconnect, and GetServerTime methods
}
public class Connector<T> : IConnector<T>
{
private T channel;
public Connector(string endpoint)
{
// ...build channel
}
public T Channel
{
get { return channel; }
}
public void Connect()
{
// ...connect to server
}
public void Disconnect()
{
// ...disconnect from server
}
}
public class ConnectorBuilder<T> : IConnectorBuilder<T>
{
private string endpoint;
public ConnectorBuilder(string endpoint)
{
this.endpoint = endpoint;
}
public IConnector<T> GetConnector()
{
Connector<T> connector = new Connector<T>(endpoint);
// If reliability was requested, build the ChannelWatchDog: Following line does not compile:
// ChannelWatchDog<T> watchDog = new ChannelWatchDog<T>(connector);
return connector;
}
public IConnectorBuilder<T> MakeReliable()
{
// save various parameters required to build the ChannelWatchDog
return this;
}
}
public static class ConnectorBuilderFactory
{
public static IConnectorBuilder<T> NewBuilder<T>(string endpoint)
{
return new ConnectorBuilder<T>(endpoint);
}
}
Así, en primer lugar, si usted encuentra el método GetConnector en la clase ConnectorBuilder, verá la línea comentada de código, que no se compila si no comentada. Esta línea es la esencia de mi problema. El problema podría ser obvio a partir del código, pero voy a tratar de explicar de todos modos en caso de que no lo es:
que tienen una clase interna (ChannelWatchDog) que necesita una IConnector. Pero no solo cualquier IConnector, un IConnector, porque aparte de los métodos de IConnector no genéricos, también necesita el método GetServerTime de la interfaz IConnectionMaintainable.
Para simplificar la construcción de conectores, esperaba implementar un generador usando el patrón Expression Builder (la interfaz IConnectionBuilder). Sin embargo, quiero ser capaz de construir cualquier IConnector, no solo IConnector <IConnectionMaintainable>. Por lo tanto, no puedo restringir T en IConnectorBuilder de la misma forma que lo limito para ChannelWatchDog. Al carecer de esta restricción, no tengo forma de compilarla cuando se llama a GetConnector. Agregar la restricción al método MakeReliable no ayuda.
Así que, esencialmente la razón por la que envió esta pregunta era que quería hacer algo que es aparentemente imposible. Yo quería que la ChannelWatchDog y la clase ConnectorBuilder a ser algo como esto:
public class ChannelWatchDog
{
private IConnector<IConnectionMaintainable> connector;
public ChannelWatchDog(IConnector<IConnectionMaintainable> connector /*various other parameters*/)
{
this.connector = connector;
}
// ...methods that use connector's Connect, Disconnect, and GetServerTime methods
}
public class ConnectorBuilder<T> : IConnectorBuilder<T>
{
private string endpoint;
public ConnectorBuilder(string endpoint)
{
this.endpoint = endpoint;
}
public IConnector<T> GetConnector()
{
Connector<T> connector = new Connector<T>(endpoint);
// If reliability was requested, build the ChannelWatchDog: Following line does not compile:
ChannelWatchDog watchDog = new ChannelWatchDog((IConnector<IConnectionMaintainable>)connector);
return connector;
}
public IConnectorBuilder<TReliable> MakeReliable<TReliable>() where TReliable : T, IConnectionMaintainable
{
// save various parameters required to build the ChannelWatchDog
return (IConnectorBuilder<TReliable>)this;
}
}
Pero el elenco de IConnector falla en tiempo de ejecución.
De modo que fue mucho más largo de lo que había pensado originalmente. Si has llegado tan lejos en la lectura, entonces ya tienes mi agradecimiento :) Cualquier idea es bienvenida, incluida la reestructuración del código.
BTW, Al no haber encontrado una solución para esto, creé diferentes ConnectorBuilders (en este caso, un ReliableConnectorBuilder) y diferentes métodos de fábrica en la fábrica. Pero no me gusta mucho esta solución.
EDIT: Solo para aclarar y reiterar: No puedo restringir el IConnector ni el ConnectionBuilder ya que estos necesitan para apoyar los casos en los que no se implementa la interfaz IConnectionMaintainable.
¿Cuál es el error de compilación en la línea que no se compila y ... qué línea es la que falla en el lanzamiento en tiempo de ejecución? tiene 2 preguntas diferentes (de hecho 2 diferentes) publicadas aquí. ¿cual es cual? ¿También puede tratar de concluir la pregunta con una pregunta real en lugar de simplemente declaraciones? – Maslow
@Maslow, la línea comentada no se compila porque el conector no está restringido según lo requerido por ChannelWatchDog. Mi esperada solución para esto, que es el conector de fundición <T> al conector <IConnectionMaintainable> es lo que falla en el tiempo de ejecución. Es decir, la línea que en el primer ejemplo no compila, es la misma línea que en el segundo ejemplo, después de la modificación, arroja una excepción en el tiempo de ejecución. Y no, no son 2 preguntas separadas. Es una pregunta en la que proporciono uno de mis intentos de solución que falló en tiempo de ejecución. – joniba