C# 如何不编写哑重载,而是在重载函数中处理大量转换

C# 如何不编写哑重载,而是在重载函数中处理大量转换,c#,casting,overloading,C#,Casting,Overloading,我想设计一些东西,让两件事之间的距离。然而,这些事情可以以许多令人讨厌的形式表现出来 假设我们有一组类。(不一定是基础和派生的) 猫,酷猫,真的酷猫 他们都有办法进入一个职位。我想写一个函数调用'DistanceBetween',得到猫之间的距离 我可以进行重载: public static float DistanceBetween(Cat cat1, Cat cat2) { return Mathf.Abs(cat1.position - cat2.position); } pub

我想设计一些东西,让两件事之间的距离。然而,这些事情可以以许多令人讨厌的形式表现出来

假设我们有一组类。(不一定是基础和派生的) 猫,酷猫,真的酷猫

他们都有办法进入一个职位。我想写一个函数调用'DistanceBetween',得到猫之间的距离

我可以进行重载:

public static float DistanceBetween(Cat cat1, Cat cat2)
{
    return Mathf.Abs(cat1.position - cat2.position);
}

public static float DistanceBetween(CoolCat cat1, CoolCat cat2)
{
    return Mathf.Abs(cat1.transform.position, cat2.transform.position);
}

// ... etc...
然而,在某些情况下,我需要知道一只猫和一只酷猫之间的距离,或者一只酷猫和一只真正的酷猫之间的距离。这意味着

public static float DistanceBetween(Cat cat1, CoolCat cat2)
{
    return Mathf.Abs(cat1.position, cat2.transform.position);
}

public static float DistanceBetween(CoolCat cat1, ReallyCoolCat cat2)
{
    return Math.Abs(cat1.tranform.position, cat2.kittyVariables.position);
}

// ... etc ...
但这似乎是任意的,因为我可以重新排列参数的顺序,而我的函数将无法工作。所以我必须做

public static float DistanceBetween(CoolCat cat1, Cat cat2)
{
    return Mathf.Abs(cat1.tranform.position, cat1.position);
}

public static float DistanceBetween(ReallyCoolCat cat1, CoolCat cat2)
{
    return Math.Abs(cat1.kittyVariables.position, cat2.transform.position);
}

// ... etc ...
这意味着每只可爱小猫的代码量增加了n^2。由于我想制作多少可爱的小猫,因此这种代码增长量是不可接受的。我不能实现继承,因为我可爱的小猫(虽然名字相似)有非常不同的特性,而且是独一无二的。(我也可以添加doggies之类的)所以我想的是创建一个接口“IDistanceable”,该接口表示实现类有一个“Position”属性,并在每个kitty中实现它。但这似乎有些过分了,我想要的只是能够重新排列我的参数,使Func(a,b)等于Func(b,a)

我真的不知道该怎么办。。。这两种解决方案(编写500个函数或使接口和大量垃圾)似乎都是错误的

由于无法修改某些可爱的kitty类,该接口将无法工作


请帮帮我和我可爱的小猫!谢谢

如果不能修改类,最好将它们包装成可以修改的内容。这样,您就可以将特定于类的逻辑集中在一个地方(不同的构造函数)


这是一个纯粹的学术答案,因为@Andrews Pilser's对于几乎任何真实世界的项目来说都是非常优秀的,但这将解决任何具有任何可以想象的表示位置方式的类的问题。它大量使用lambda表达式和泛型,并且不需要控制底层类

代码是用脚本编写的,所以看起来可能有点奇怪,但它是标准的C#(版本7),可以直接捕捉到Visual Studio中。文件可用

这使用
字典
为任何可转换为
类型
存储
ToPointConverter
ToPointConverter
是从静态方法
Create
创建的,该方法接受从特定泛型
T
返回
点的lambda

如您所见,我提供了3个示例“kitty”类,每个类以完全不同的方式存储它们的位置。main函数为每一个创建一个转换器,将其存储在转换器字典中,然后计算不同“kitties”组合之间的距离。(我可能把距离函数弄错了,已经很晚了,但这只是一个小细节。)

它产生以下输出:

2.23606797749979
9.05538513813742
2.23606797749979
8.062257748255
9.05538513813742
8.062257748255

void Main()
{
//为任何可以转换的对象定义转换函数。
Add(typeof(KittyA),ToPointConverter.Create(kitty=>kitty.Location));
Add(typeof(KittyB),ToPointConverter.Create(kitty=>newpoint{X=kitty.X,Y=kitty.Y});
Add(typeof(KittyC),ToPointConverter.Create(kitty=>kitty.MyLocation));
//申报一些小猫
var kitty1=新KittyA{Location=新点{X=1,Y=1};
var kitty2=新KittyB{X=3,Y=2};
var kitty3=新KittyC{MyLocation=新点{X=2,Y=10};
//计算距离
GetDistance(kitty1,kitty2.Dump();
GetDistance(kitty1,kitty3.Dump();
GetDistance(kitty2,kitty1.Dump();
GetDistance(kitty2,kitty3).Dump();
GetDistance(kitty3,kitty1.Dump();
GetDistance(kitty3,kitty2.Dump();
}
专用字典转换器=新字典();
//执行的辅助函数将传递的对象转换为点,并计算它们之间的距离。
专用双GetDistance(对象obj1、对象obj2)
{
var point1=GetConvrterFor(obj1).Convert(obj1);
var point2=GetConvrterFor(obj2).Convert(obj2);
返回Math.Sqrt(Math.Pow(point2.X-point1.X,2)+Math.Pow(point2.Y-point1.Y,2));
}
//另一个帮助程序,用于获取传入的对象实例的IToPointConverter。
私有IToPointConverter GetConverterFor(对象obj)=>转换器[obj.GetType()];
//这个泛型类存储一个lambda表达式,该表达式将T转换为一个点
公共类ToPointConverter:IToPointConverter
{
公共静态拓扑转换器创建(Func转换)
{
返回新的拓扑转换器(转换);
}
专用拓扑转换器(Func转换)
{
_转换=转换;
}
私有函数转换;
公共点转换(T obj)=>U转换(obj);
转换(对象obj)=>Convert((T)obj);
}
//转换器的非泛型接口(因此不同的封闭泛型类型可以存储在同一个字典中,并调用其转换方法。)
公共接口点转换器
{
点转换(对象obj);
}
//只是一个标准的结构来容纳一个位置。您可以使用框架中的任何本地location类。
公共结构点
{
公共int X;
公共智力;
}
//一些kitty类的例子
公务舱KittyA
{
公共点位置{get;set;}
}
公营小猫
{
公共整数X{get;set;}
公共整数Y{get;set;}
}
公共类KittyC
{
公共点MyLocation{get;set;}
}

如果他们都有办法访问某个位置,则创建一个名为
IPositio的界面
class CatWrapper
{
    private int position { get; set; }

    public CatWrapper(Cat cat) { ... }
    public CatWrapper(CoolCat cat) { ... }
    public CatWrapper(ReallyCoolCat cat) { ... }

    public DistanceFrom(CatWrapper other) { ... }
}
void Main()
{
    //Define conversion functions for anything that can be converted.
    converters.Add(typeof(KittyA), ToPointConverter<KittyA>.Create(kitty => kitty.Location));
    converters.Add(typeof(KittyB), ToPointConverter<KittyB>.Create(kitty => new Point { X = kitty.X, Y = kitty.Y }));
    converters.Add(typeof(KittyC), ToPointConverter<KittyC>.Create(kitty => kitty.MyLocation));

    //Declare some kitties
    var kitty1 = new KittyA { Location = new Point { X = 1, Y = 1 } };
    var kitty2 = new KittyB { X = 3, Y = 2 };
    var kitty3 = new KittyC { MyLocation = new Point { X = 2, Y = 10 } };

    //Calculate the distances
    GetDistance(kitty1, kitty2).Dump();
    GetDistance(kitty1, kitty3).Dump();

    GetDistance(kitty2, kitty1).Dump();
    GetDistance(kitty2, kitty3).Dump();

    GetDistance(kitty3, kitty1).Dump();
    GetDistance(kitty3, kitty2).Dump();
}
private Dictionary<Type, IToPointConverter> converters = new Dictionary<Type, IToPointConverter>();

//A helper function that does the converts the passed objects in to Points, and calculates the distance between them.
private double GetDistance(object obj1, object obj2)
{
    var point1 = GetConvrterFor(obj1).Convert(obj1);
    var point2 = GetConvrterFor(obj2).Convert(obj2);

    return Math.Sqrt(Math.Pow(point2.X - point1.X, 2) + Math.Pow(point2.Y - point1.Y, 2));
}

//Another helper that gets the IToPointConverter for the object instance passed in.
private IToPointConverter GetConvrterFor(object obj) => converters[obj.GetType()];

//This generic class stores a lambda expression that converters from T to a Point
public class ToPointConverter<T> : IToPointConverter
{
    public static ToPointConverter<T> Create(Func<T, Point> conversion)
    {
        return new ToPointConverter<T>(conversion);
    }

    private ToPointConverter(Func<T, Point> conversion)
    {
        _conversion = conversion;
    }

    private Func<T, Point> _conversion;

    public Point Convert(T obj) => _conversion(obj);

    Point IToPointConverter.Convert(object obj) => Convert((T)obj);
}

//The non-generic interface for the converter (so different closed generic types can be stored in the same dictionary, and have their Convert method called.)
public interface IToPointConverter
{
    Point Convert(object obj);
}

//Just a standard structure to hold a location.  You would use whatever native location class your framework has.
public struct Point
{
    public int X;
    public int Y;
}


//Some example kitty classes
public class KittyA
{
    public Point Location { get; set; }
}

public class KittyB
{
    public int X { get; set; }
    public int Y { get; set; }
}

public class KittyC
{
    public Point MyLocation { get; set; }
}