如何不编写愚蠢的过载,但要在超载功能中处理大量铸造



我想设计一些能够在两件事之间距离的东西。但是,这些东西可以以许多令人讨厌的形式表现出来。

假设我们有一组课程。(不一定是基础和衍生)猫,酷猫,真正的coolcat

所有人都有一种访问职位的方法。我想编写一个函数调用的"距离",该函数获得猫之间的距离。

我可以超载:

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...

但是,然后我将需要知道猫与猫猫之间的距离,或者是coolcat和coolcat和真正的coolcat之间的距离。这意味着...

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。由于我想制造多少可爱的小猫,因此这一数量的代码增长是不可接受的。我无法实施继承,因为我的可爱小猫(尽管名称相似)具有非常不同的功能,并且是独一无二的。(我也可以添加狗狗等等。)因此,我想创建一个" IDistanceant"的接口,说实现类具有"位置"属性,并在每个小猫中实现它。但这似乎开始看起来像是过度杀伤,我想要的只是可以重新排列我的论点并制作与弹性(b,a)...

的功能(a,b)...

我真的不知道该怎么做...两个解决方案(编写500个功能或制作界面和很多垃圾)似乎都错了。

由于无法修改一些可爱的小猫班,该界面将无法使用...

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

如果您无法修改类,最好将它们包裹在可以修改的东西中。这样,您可以将特定于类的逻辑集中到一个地方(不同的构造函数)。

class CatWrapper
{
    private int position { get; set; }
    public CatWrapper(Cat cat) { ... }
    public CatWrapper(CoolCat cat) { ... }
    public CatWrapper(ReallyCoolCat cat) { ... }
    public DistanceFrom(CatWrapper other) { ... }
}

这是一个纯粹的学术答案,因为 @andrews Pilser在几乎任何现实世界中都非常出色,但这将解决它的任何一个任何表示位置的可疑方法。它大量使用了lambda-Expressions和Generic,并且不需要对基础类别的控制。

该代码是用LinqPad编写的,因此看起来有些奇怪,但是标准C#(版本7)可以直接进入Visual Studio。文件可用。

这使用Dictionary存储可以转换为Point的任何TypeToPointConverterToPointConverter是从静态方法Create创建的,该方法接受lambda,该lambda从特定的通用T返回Point

您可以看到,我提供了3个示例"小猫"类,每个类别以完全不同的方式存储他们的位置。主函数为每个函数创建一个转换器,将其存储在转换器的字典中,然后计算"小猫"的不同组合之间的距离。(我可能距离我的距离功能错误,这已经很晚了,但这是一个小细节。)

它产生此输出:

2.23606797749979
9.05538513813742
2.23606797749979
8.06225774829855
9.05538513813742
8.06225774829855

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

最新更新