Defining implicit and explicit casts for C# interfaces
Is there a way to write interface-based code (i.e. using interfaces rather than classes as the types accepted and passed around) in C# without giving up the use of things like implicit casts? Here's some sample code - there's been a lot removed, but these are the relevant portions.
public class Game
{
public class Va开发者_Python百科riantInfo
{
public string Language { get; set; }
public string Variant { get; set; }
}
}
And in ScrDictionary.cs, we have...
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};
}
}
And the interface...
public interface IScrDictionary
{
string Language { get; set; }
string Variant { get; set; }
}
I want to be able to use IScrDictionary
instead of ScrDictionary
, but still be able to implicitly convert a ScrDictionary
to a Game.VariantInfo
. Also, while there may be an easy way to make this work by giving IScrDictionary
a property of type Game.VariantInfo
my question is more generally: Is there a way to define casts or operator overloading on interfaces? (If not, what is the proper C# way to maintain this functionality without giving up interface-oriented design?)
You cannot define casts or operator overloading on interfaces. Since an interface is a contract that describes the members which will always be available (either as an explicit cast to that interface or as public members) and nothing more you cannot rely on interfaces to contain any sort of built in logic such as how to cast or how operators will perform with that interface.
You can still inherit from an abstract base class which implements the interface and provides the logic you need for casts or operator overloading. This doesn't violate interface oriented design. Classes which do not inherit from the common base class but implement the interface will still need to independently implement their own implicit casts and operator overloads. If you wish to centralize the logic for working with classes that commonly implement an interface you can do so in C# 3.0+/.NET Fx 3.5 with extension methods (or in previous versions with static methods). Below I demonstrate this with a utility class and two classes, Foo and Bar, which don't have a common ancestor. They share the code which comprises the utility function Add so you don't have to repeat this implementation in both 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();
}
}
If your interfaces contained more than just contracts that would be violating design by contract.
One way to do this is if there is a cast/conversion you'll often need is to define an explicit method on your interface e.g.
public interface ISomeInterface
{
TargetType ToTargetType();
}
Then in an abstract base class you can define an implicit/explicit cast and have the cast operator just call the interface method in which you define your actual cast logic e.g.
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();
}
}
This way you ensure that implementations provide a means to cast to the necessary type. Thus purely interface driven code can call the interface method to do the conversion. But your code that uses concrete implementations of the interface will have the cast operators defined and can use them instead
精彩评论