2010-05-05 8 views
19

Existe-t-il un moyen d'écrire du code basé sur une interface (c'est-à-dire en utilisant des interfaces plutôt que des classes comme types acceptés et transmis) en C# sans abandonner l'utilisation de distributions implicites? Voici un exemple de code - il y en a beaucoup supprimé, mais ce sont les parties pertinentes.Définition des lancements implicites et explicites pour les interfaces C#

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

Et ScrDictionary.cs, nous avons ...

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}; 
    } 
} 

Et l'interface ...

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

Je veux être en mesure d'utiliser IScrDictionary au lieu de ScrDictionary, mais toujours être en mesure de convertir implicitement un ScrDictionary en Game.VariantInfo. En outre, alors qu'il peut y avoir un moyen facile de faire ce travail en donnant IScrDictionary une propriété de type Game.VariantInfo ma question est plus généralement: Existe-t-il un moyen de définir les casques ou la surcharge de l'opérateur sur les interfaces? (Sinon, quel est le bon moyen de maintenir cette fonctionnalité en C# sans renoncer à la conception orientée interface?)

+3

Hasbro n'a pas toujours joué bien quand les gens utilisent le nom Scrabble ... –

Répondre

6

Vous ne pouvez pas définir les surcharges ou la surcharge de l'opérateur sur les interfaces. Puisqu'une interface est un contrat qui décrit les membres qui seront toujours disponibles (soit comme une distribution explicite vers cette interface ou comme des membres publics) et rien de plus, vous ne pouvez pas compter sur des interfaces pour contenir toute sorte de logique intégrée comme la façon de lancer ou comment les opérateurs vont fonctionner avec cette interface.

Vous pouvez toujours hériter d'une classe de base abstraite qui implémente l'interface et fournit la logique dont vous avez besoin pour les conversions ou les surcharges d'opérateur. Cela ne viole pas la conception orientée interface. Les classes qui n'héritent pas de la classe de base commune mais implémentent l'interface devront toujours implémenter indépendamment leurs propres lancers implicites et surcharges d'opérateurs. Si vous souhaitez centraliser la logique pour travailler avec des classes implémentant couramment une interface, vous pouvez le faire dans C# 3.0 + /. NET Fx 3.5 avec des méthodes d'extension (ou dans les versions précédentes avec des méthodes statiques). Ci-dessous, je le démontre avec une classe d'utilité et deux classes, Foo et Bar, qui n'ont pas d'ancêtre commun. Ils partagent le code qui comprend la fonction d'utilitaire Ajouter afin que vous n'ayez pas à répéter cette implémentation dans les deux classes.

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 vos interfaces contiennent plus que des contrats qui violeraient la conception par contrat.

2

Une façon de procéder est la suivante: s'il y a un cast/une conversion, vous aurez probablement besoin de le faire en tant que méthode sur votre Inteface, par exemple.

public interface ISomeInterface 
{ 
    TargetType ToTargetType(); 
} 

Puis, dans une classe de base abstraite, vous pouvez définir un implict/explicite et ont moulé la mise en œuvre de ToTargetType() simplement appeler l'opérateur moulé par exemple

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

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

De cette façon, vous vous assurez que les implémentations fournissent un moyen de jeter le type nécessaire pour que le code d'interface conduit purement peut appeler la méthode d'interface pour effectuer la conversion. Mais votre code qui utilise des implémentations concrètes de cette interface qui ont les opérateurs de cast définis peuvent les utiliser à la place

Questions connexes