2010-05-05 3 views
19

Есть ли способ написать интерфейс на основе интерфейса (т. Е. Использовать интерфейсы, а не классы, как типы, принятые и переданные) на C#, не отказываясь от использования таких вещей, как неявные приведения? Вот пример кода - там было много удаленных, но это соответствующие части.Определение неявных и явных преобразований для интерфейсов C#

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

И в ScrDictionary.cs, у нас есть ...

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

И интерфейс ...

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

Я хочу, чтобы иметь возможность использовать IScrDictionary вместо ScrDictionary, но все же иметь возможность неявно преобразовывать ScrDictionary в Game.VariantInfo. Кроме того, хотя может быть простой способ сделать эту работу, давая IScrDictionary свойство типа Game.VariantInfo, мой вопрос в более общем виде: Есть ли способ определить перегрузки или перегрузку оператора на интерфейсах? (Если нет, то каков правильный способ C# поддерживать эту функциональность, не отказываясь от интерфейса-ориентированного дизайна?)

+3

Hasbro не всегда играл хорошо, когда люди использовали имя Scrabble ... –

ответ

6

Вы не можете определить трансляции или перегрузку оператора на интерфейсах. Поскольку интерфейс - это контракт, который описывает элементы, которые всегда будут доступны (либо как явное приведение к этому интерфейсу, либо как публичные члены), и не более того вы не можете полагаться на интерфейсы, чтобы содержать какую-либо встроенную логику, например, как делать или как операторы будут работать с этим интерфейсом.

Вы по-прежнему можете унаследовать от абстрактного базового класса, который реализует интерфейс и предоставляет логику, необходимую для бросков или перегрузки оператора. Это не нарушает ориентированный на интерфейс дизайн. Классы, которые не наследуются от общего базового класса, но реализуют интерфейс, по-прежнему должны самостоятельно реализовать свои собственные неявные броски и перегрузки операторов. Если вы хотите централизовать логику работы с классами, которые обычно реализуют интерфейс, вы можете сделать это в C# 3.0 + /. NET Fx 3.5 с помощью методов расширения (или в предыдущих версиях со статическими методами). Ниже я демонстрирую это с помощью класса утилиты и двух классов Foo и Bar, которые не имеют общего предка. Они совместно используют код, который содержит функцию утилиты Add, поэтому вам не нужно повторять эту реализацию в обоих классах.

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

Если ваши интерфейсы содержат больше, чем просто контракты, которые будут нарушать дизайн по контракту.

2

Один из способов сделать это, если есть приведение/преобразование, которое, скорее всего, вам нужно будет определить, как метод на вашем inteface, например.

public interface ISomeInterface 
{ 
    TargetType ToTargetType(); 
} 

Затем в абстрактном базовом классе можно определить неявным/явное приведение и есть осуществление ToTargetType() просто вызвать оператор приведения например

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

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

Таким образом, вы убедитесь, что реализации обеспечивают средство, чтобы бросить к нужному типу, так что чисто интерфейс управляемый код может вызывать метод интерфейса, чтобы сделать преобразование. Но ваш код, который использует конкретные реализации этого интерфейса, которые имеют определенные операторы литья, может использовать их вместо этого.

Смежные вопросы