2010-05-05 11 views
19

¿Hay alguna manera de escribir código basado en interfaz (es decir, usando interfaces en lugar de clases como los tipos aceptados y transmitidos) en C# sin renunciar al uso de elementos como moldes implícitos? Aquí hay un código de muestra: se ha eliminado mucho, pero estas son las partes relevantes.Definición de conversiones implícitas y explícitas para interfaces C#

public class Game 
{ 
    public class VariantInfo 
    { 
     public string Language { get; set; } 
     public string Variant { get; set; } 
    } 
} 

Y en ScrDictionary.cs, tenemos ...

public class ScrDictionary: IScrDictionary 
{ 
    public string Language { get; set; } 
    public string Variant { get; set; } 

    public static implicit operator Game.VariantInfo(ScrDictionary s) 
    { 
     return new Game.VariantInfo{Language=sd.Language, Variant=sd.Variant}; 
    } 
} 

Y la interfaz ...

public interface IScrDictionary 
{ 
    string Language { get; set; } 
    string Variant { get; set; } 
} 

Quiero ser capaz de utilizar IScrDictionary en lugar de ScrDictionary, pero todavía podrá convertir implícitamente un ScrDictionary a un Game.VariantInfo. Además, si bien puede haber una manera fácil de hacer que esto funcione dando a IScrDictionary una propiedad del tipo Game.VariantInfo mi pregunta es más general: ¿Hay alguna manera de definir las conversiones o la sobrecarga del operador en las interfaces? (En caso negativo, ¿cuál es la forma correcta de C# para mantener esta funcionalidad sin renunciar al diseño orientado a la interfaz?)

+3

Hasbro no siempre ha jugado bien cuando las personas usan el nombre Scrabble ... –

Respuesta

6

No se puede definir conversiones o sobrecarga del operador en las interfaces. Dado que una interfaz es un contrato que describe los miembros que siempre estarán disponibles (ya sea como un molde explícito para esa interfaz o como miembros públicos) y nada más no se puede confiar en que las interfaces contengan cualquier tipo de lógica incorporada, como la forma de transmitir o cómo los operadores realizarán con esa interfaz.

Aún puede heredar de una clase base abstracta que implementa la interfaz y proporciona la lógica que necesita para conversiones o sobrecarga del operador. Esto no viola el diseño orientado a la interfaz. Las clases que no heredan de la clase base común pero implementan la interfaz todavía necesitarán implementar independientemente sus propios moldes implícitos y sobrecargas del operador. Si desea centralizar la lógica para trabajar con clases que comúnmente implementan una interfaz, puede hacerlo en C# 3.0 + /. NET Fx 3.5 con métodos de extensión (o en versiones anteriores con métodos estáticos). A continuación, demuestro esto con una clase de utilidad y dos clases, Foo y Bar, que no tienen un ancestro común. Comparten el código que comprende la función de utilidad Agregar para que no tenga que repetir esta implementación en ambas clases.

public interface IInterface 
{ 
    int X { get; set; } 
    int Y { get; set; } 
} 

public static class IInterfaceTHelper 
{ 
    public static IInterface Add<T>(this IInterface a, IInterface b) 
     where T : new() 
    { 
     var ret = (IInterface)new T(); 
     ret.X = a.X + b.X; 
     ret.Y = a.Y + b.Y; 
     return ret; 
    } 
} 

class Foo : IInterface 
{ 
    public int X { get; set; } 
    public int Y { get; set; } 

    public static IInterface operator +(Foo a, IInterface b) 
    { 
     return a.Add<Foo>(b); 
    } 
} 

class Bar : IInterface 
{ 
    public int X { get; set; } 
    public int Y { get; set; } 

    public static IInterface operator +(Bar a, IInterface b) 
    { 
     return a.Add<Bar>(b); 
    } 
} 

class Program 
{ 
    static void Main(string[] args) 
    { 
     var foo = new Foo { X = 5, Y = 3 }; 
     var bar = new Bar { X = 3, Y = 5 }; 

     var result = foo + bar; 
     Console.WriteLine(result.GetType().Name + " " + result.X + " " + result.Y); 
     result = bar + foo; 
     Console.WriteLine(result.GetType().Name + " " + result.X + " " + result.Y); 

     Console.ReadLine(); 
    } 
} 

Si sus interfaces contenían algo más que contratos que estarían violando el diseño por contrato.

2

Una forma de hacerlo es si hay un molde/conversión que es probable que a menudo necesite definir como un método en su interfaz, p.

public interface ISomeInterface 
{ 
    TargetType ToTargetType(); 
} 

Luego, en una clase base abstracta puede definir un/conversión explícita implict y tener la implementación de ToTargetType() a llamarlo el operador de conversión, por ejemplo,

public abstract class SomeAbstractClass : ISomeInterface 
{ 
    public TargetType ToTargetType() 
    { 
    return (TargetType)this; 
    } 

    public static explicit operator TargetType(SomeAbstractClass obj) 
    { 
    //Actual cast logic goes here 
    } 
} 

De esta manera se asegura que las implementaciones proporcionan un medio para emitir al tipo necesario para que puramente impulsada interfaz de código puede llamar al método de interfaz para hacer la conversión. Pero su código que usa implementaciones concretas de esa interfaz que tienen los operadores de reparto definidos puede usarlos en su lugar

Cuestiones relacionadas