在接口中使用泛型

本文关键字:泛型 接口 | 更新日期: 2023-09-27 18:07:53

更新:

海因茨是对的。AutoCAD多段线是参照类型,而不是结构。说得好。但我已经简化了这个场景,因为我在实际应用程序中处理的是一个结构的AutoCAD对象。因此,请将两者视为结构而非引用类型。


在这种情况下,我正在寻找正确的方法,如果有人能阐明或帮助我更好地理解,我将不胜感激。

数据访问层中有一个接口,它有两个实现来处理两个不同的提供者:AutoCad和SketchupAPI。

interface IEntity
{
    void object GetPoly();
    void void   InsertPoly(object poly);
}
class AutocadEntity
{
    void object GetPoly()
    {
         //calling Autocad APIs
         return Autocad Polyline object
    }
    void InsertPoly(object poly){...}
}

GetPoly的Autocad实现将返回Polyline对象,因为这是Autocad API中定义的多段线,而Sketchup将返回Face对象。

我已经将返回类型(和参数(定义为对象来处理这些不同的类型。成本是装箱/开箱时的性能问题。在return/parameter是object[]的地方,它更大胆地显示了自己。

我首先想知道让方法返回/参数类型通用是解决方案,但后来我认为这不会是因为实现是特定于类型的。

在接口中使用泛型

成本是装箱/开箱时的性能问题。

我不这么认为。折线是一个类,而不是一个结构。因此,不涉及拳击。

如果这实际上是应用程序的性能瓶颈,那么您的性能就会在其他地方丢失。一如既往:在优化之前先进行测量,否则最终可能会优化错误的东西。

我认为你的解决方案非常好。您可以使用泛型并从IEntity<Polyline>派生AutocadEntity,但这有什么意义?由于多段线/面在界面中同时用作输入和输出参数,因此可以使IEntity既不是协变的也不是逆变的。因此,IEntity<Polyline>IEntity<Face>最常见的基本类型是object,这意味着如果您不知道具体类型,就不能再只传递一个通用的IEntity

尝试使用适配器模式将PolyLine和Face类型调整为您喜欢使用的单个类型。例如:

public abstract class BasePoly
{
    public abstract double X { get; set; }
    public abstract double Y { get; set; }
    public abstract double Width { get; set; }
    public abstract double Height { get; set; }
}
public abstract class BasePoly<T> : BasePoly
{
    public T poly { get; private set; }
    protected BasePoly(T poly) { this.poly = poly; }
}
public class PolyLineAdapter : BasePoly<PolyLine>
{
    public PolyLineAdapter(PolyLine poly) : base(poly) {}
    // override abstracts and forward to inner PolyLine instance at 'this.poly'
    public override double X { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
    public override double Y { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
    public override double Width { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
    public override double Height { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
}
public class FaceAdapter : BasePoly<Face>
{
    public FaceAdapter(Face poly) : base(poly) {}
    // override abstracts and forward to inner Face instance at 'this.poly'
    public override double X { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
    public override double Y { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
    public override double Width { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }
    public override double Height { get { throw new NotImplementedException(); } set { throw new NotImplementedException(); } }

}
interface IEntity
{
    BasePoly GetPoly();
    void   InsertPoly(BasePoly poly);
}
public abstract class Entity<TEntity> : IEntity
    where TEntity : BasePoly
{
    public BasePoly GetPoly()
    {
        return this.GetExternalPoly();
    }
    public abstract TEntity GetExternalPoly();
    public void InsertPoly(BasePoly poly)
    {
        this.InsertExternalPoly((TEntity) poly);
    }
    public abstract void InsertExternalPoly(TEntity poly);
}
public class AutocadEntity : Entity<PolyLineAdapter>
{
    public override PolyLineAdapter GetExternalPoly()
    {
        throw new NotImplementedException();
    }
    public override void InsertExternalPoly(PolyLineAdapter poly)
    {
        throw new NotImplementedException();
    }
}
public class SketchupEntity : Entity<FaceAdapter>
{
    public override FaceAdapter GetExternalPoly()
    {
        throw new NotImplementedException();
    }
    public override void InsertExternalPoly(FaceAdapter poly)
    {
        throw new NotImplementedException();
    }
}
// fills for third party classes
public class PolyLine {}
public class Face {}

使用适配器模式,您可以提供一个代理层,使两个第三方类型符合您想要使用的类型。

请记住,此设计假设您一次只能使用一种类型的第三方引擎。如果您将同时使用两个引擎,请进行以下更改:

public class BasePoly
{
    public double X { get; set; }
    public double Y { get; set; }
    public double Width { get; set; }
    public double Height { get; set; }
}
interface IEntity
{
    BasePoly GetPoly();
    void InsertPoly(BasePoly poly);
}
public abstract class Entity : IEntity
{
    public abstract BasePoly GetPoly();
    public abstract void InsertPoly(BasePoly poly);
}
public class AutocadEntity : Entity
{
    public override BasePoly GetPoly()
    {
        // retrieve external type, convert it to BasePoly and return that
        throw new NotImplementedException();
    }
    public override void InsertPoly(BasePoly poly)
    {
        // convert BasePoly to external type and insert that
        throw new NotImplementedException();
    }
}
public class SketchupEntity : Entity
{
    public override BasePoly GetPoly()
    {
        // retrieve external type, convert it to BasePoly and return that
        throw new NotImplementedException();
    }
    public override void InsertPoly(BasePoly poly)
    {
        // convert BasePoly to external type and insert that
        throw new NotImplementedException();
    }
}
// fills for third party classes
public class PolyLine {}
public class Face {}

此外,如果您担心适配器装箱或转换操作的成本(在您实际测量并确定是否需要优化之前,我不会这么做(,那么您可以将适配器模式应用于使用IEntity的调用方,而不是PolyLineAdapter/FaceAdapterAutocadEntity/SchetchupEntity类型本身。本质上,构建一个插件引擎。您可以使用泛型来抽象这两个实现之间的常见习惯用法。

下面是一个dotnetfiddle示例:https://dotnetfiddle.net/UsFPM7

由于您有两个不同的类来实现接口,我认为您最好的选择是使接口通用。

interface IEntity<T>
{
   T GetPoly();
   void InsertPoly(T poly);
}
class AutocadEntity : IEntity<Polyline>
{
    Polyline GetPoly(){...}
    void InsertPoly(Polyline poly) {...}
}