为C#接口定义隐式和显式转换

22

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

而且这个接口在Python 3.x中已经被删除了,你需要使用tkinter.messagebox代替。

 public interface IScrDictionary
 {
     string Language { get; set; }
     string Variant { get; set; }
 }
我想要使用IScrDictionary代替ScrDictionary,但仍然能够将ScrDictionary隐式转换为Game.VariantInfo。同时,虽然通过给IScrDictionary一个类型为Game.VariantInfo的属性可以轻松解决此问题,但我的问题更普遍:是否有一种方法在接口上定义强制转换或运算符重载?(如果没有,使用正确的C#方式来保持此功能而不放弃面向接口的设计是什么?)

3
孩之宝并不总是友好地对待使用“Scrabble”名称的人…… - Austin Salonen
2个回答

7

您不能在接口上定义强制转换或运算符重载。由于接口是描述始终可用的成员(作为该接口的显式强制转换或公共成员)以及没有任何内置逻辑的契约,因此您不能依赖接口包含任何强制转换或运算符如何执行的内置逻辑。

您仍然可以从实现接口并提供所需强制转换或运算符重载逻辑的抽象基类继承。这不违反面向接口的设计。没有继承公共基类但实现接口的类将仍需要独立实现其自己的隐式转换和运算符重载。如果您希望集中处理通常实现接口的类的工作逻辑,则可以在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();
    }
}

如果您的接口除了合同之外还包含其他内容,那么这将违反契约设计原则。

5

有一种方法可以实现这一点,即如果您经常需要进行强制转换,则可以在接口上定义一个显式方法,例如:

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

这样您就确保了实现提供了将类型转换为必要类型的方法。因此,纯接口驱动的代码可以调用接口方法来进行转换。但是,使用接口的具体实现的代码将定义强制类型转换运算符,并且可以使用它们。


1
这就是我所做的...也许不是最优雅的代码,但它确实可以工作。我应该提到,我尝试编写了一个运算符重载来将接口转换为受限类型,但编译器神灵不允许你这样做。 有一个特定的警告,关于不允许进行与接口之间的类型转换。 - HarvesteR

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接