Quiero crear una estructura de Servicios todos ellos derivados de la interfaz IService que tiene un único método Perform
Para permitir que las clases derivadas definan el tipo de argumentos que necesitan las implementaciones específicas de Perform, encapsulo dichos argumentos en una interfaz llamada IConfig
El problema es que no puedo acceder a las propiedades de la clase derivada en mis implementaciones personalizadas de Perform porque me veo obligado a escribir el argumento como IConfig
¿Hay alguna forma de anular un método de interfaz/clase abstracta con argumentos de clase derivada de la clase de argumento del método de la interfaz?
Ejemplo:
interface IConfig {} class Config : IConfig { public int Property; } interface IService { void Perform(IConfig config); } class Service : IService { void IService.Perform(IConfig config) { config.Property; } } Con la implementación del Service anterior, aparece el error:
'IConfig' no contiene una definición para 'Propiedad'
Si lo cambio a esto:
class Service : IService { void IService.Perform(Config config) { config.Property; } }me salen los errores:
'Servicio' no implementa el miembro de interfaz 'IService.Perform(IConfig)'
y
'Service.Perform(Config)' en la declaración de interfaz explícita no se encuentra entre los miembros de la interfaz que se pueden implementar
Me pregunto si hay algo como:
void IService.Perform((IConfig)Config config)o
void IService.Perform(Config config as IConfig)o
(Config)config.PropertyEste es el caso perfecto para usar genéricos.
interface IConfig { } class Config : IConfig { public int Property; } interface IService<T> where T : IConfig { void Perform(T config); } class Service : IService<Config> { public void Perform(Config config) { config.Property; } }Referencia de genéricos: https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/
where referencia - https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/where-generic-type-constraint
Tenga en cuenta que al hacer que una clase o interfaz sea genérica, no puede reducirla a una forma no genérica como IService sin cambiarla para que la forma genérica se extienda desde una base no genérica de la siguiente manera:
interface IService { } interface IService<T> : IService where T : IConfig { void Perform(T config); } Esto elimina el acceso a las propiedades de la forma genérica cuando se convierte en IService (lo cual es obvio cuando lo piensa). Tendrá que volver a convertirlo en su tipo genérico correcto ( IService<Config> ) para acceder a esas propiedades que podrían causar un dolor de cabeza al tratar con colecciones ( List<IService> por ejemplo) de múltiples tipos genéricos.
Para evitar esto, puede conformarse con usar fundición simple.
Si no le importan las restricciones de tiempo de compilación en el tipo de configuración y SÍ le importa el problema List<IService> anterior, puede optar por una alternativa de tiempo de ejecución como esta:
interface IConfig { } class Config : IConfig { public int Property; } interface IService { void Perform(IConfig config); } class Service : IService { void IService.Perform(IConfig config) { if (!(config is Config)) throw new ArgumentException("Expected config to be of type Config"); Config castedConfig = (Config)config; castedConfig.Property; } } Esto NO le impedirá llamar a Perform con una configuración del tipo incorrecto a través de restricciones de tiempo de compilación, pero lanzará una ArgumentException en tiempo de ejecución si lo llama con una configuración del tipo incorrecto.