Подтвердить что ты не робот

Определение неявных и явных заданий для интерфейсов С#

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

 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, мой вопрос в целом: Есть ли способ определить трансляции или перегрузку операторов на интерфейсах? (Если нет, то каков правильный способ С# поддерживать эту функциональность, не отказываясь от интерфейса-ориентированного дизайна?)

4b9b3361

Ответ 1

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

Вы все еще можете наследовать от абстрактного базового класса, который реализует интерфейс и предоставляет логику, необходимую для трансляции или перегрузки оператора. Это не нарушает ориентированный на интерфейс дизайн. Классы, которые не наследуются от общего базового класса, но реализуют интерфейс, по-прежнему должны самостоятельно реализовать свои собственные неявные броски и перегрузки операторов. Если вы хотите централизовать логику работы с классами, которые обычно реализуют интерфейс, вы можете сделать это в С# 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

Одним из способов сделать это является то, что если вам часто требуется преобразование/преобразование, это определить явный метод в вашем интерфейсе, например

public interface ISomeInterface
{
   TargetType ToTargetType();
}

Затем в абстрактном базовом классе вы можете определить неявное/явное приведение и заставить оператор приведения просто вызвать метод интерфейса, в котором вы определяете свою фактическую логику приведения, например

public abstract class SomeAbstractClass : ISomeInterface
{
  public TargetType ToTargetType()
  {
    // Actual cast logic goes here
    return (TargetType)this;
  }

  public static explicit operator TargetType(SomeAbstractClass obj)
  {
    return ToTargetType();
  }
}

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