C# 在接口中使用泛型

C# 在接口中使用泛型,c#,generics,boxing,unboxing,C#,Generics,Boxing,Unboxing,更新: 海因茨是对的。AutoCAD多段线是参照类型,而不是结构。说得好。但我简化了场景,因为我在实际应用程序中处理的是一个AutoCAD对象,它是struct。因此,请考虑两者都是结构而不是引用类型。 我正在寻找在这种情况下采取的正确方法,如果有人能阐明或帮助我更好地理解,我将不胜感激 数据访问层中有一个接口,它有两个实现来处理两个不同的提供者:AutoCad和Sketchup API interface IEntity { void object GetPoly(); vo

更新:

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


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

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

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实现将返回多段线对象,因为这是Autocad API中定义为多段线的对象,而Sketchup将返回面对象

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

我首先想知道将方法return/parameter类型设置为泛型是一种解决方案,但后来我认为不会,因为实现是特定于类型的

成本是装箱/拆箱发生时的性能问题

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

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


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

因为你有两个不同的类来实现这个接口,我认为你最好的办法是让这个接口通用

interface IEntity<T>
{
   T GetPoly();
   void InsertPoly(T poly);
}
class AutocadEntity : IEntity<Polyline>
{
    Polyline GetPoly(){...}
    void InsertPoly(Polyline poly) {...}
}
interface-intentity
{
T GetPoly();
空心插入多边形(T多边形);
}
类自动属性:属性
{
多段线GetPoly(){…}
void InsertPoly(多段线多边形){…}
}
尝试使用将多段线和面类型调整为您希望使用的单一类型。例如:

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 {}
另外,如果您担心适配器装箱或转换操作的成本(在您实际测量并确定是否需要优化之前,我不会这样做),然后,您可以将适配器模式应用于使用
IEntity
的调用者,而不是PolyLineAdapter/FaceAdapter或AutocadEntity/SketchupEntity类型本身。本质上,构建一个插件引擎。您可以使用泛型来抽象这两个实现之间的常用习惯用法


下面是一个dotnetfiddle示例:

为什么不使用两个不同的接口?毕竟,尽管这是一个类似的电话,对于不同的实现,函数定义是不同的。AutocadEntity是处理Autocad的类,然后是SketchupEntity,还是AutocadEntity同时处理这两个类?@Jarrett是的,还有一个SketchupEntity类也实现了处理Skethcup API的相同接口。很抱歉遗漏了这一部分。通用接口是一个很好的解决方案,但为什么您认为性能问题与装箱/拆箱有关?是
多段线
还是
结构?@Hamlet请查看更新。我认为好处在于类型安全。@Hamlet Hakobyan:是的。。。以代码复制为代价,因为您必须将所有
DoSomething(ienty)
方法分成两部分:
DoSomething(ienty)
DoSomething(ienty)
。当然,你可以做一个
DoSomething(ienty)
,但是你又开始做类型检查了,类型安全的好处消失了。@Heinzi“因此,ienty和ienty最常见的基本类型是object,这意味着如果你不知道具体的类型,你就不能再通过一般的ienty了。”你能举个例子吗?谢谢。@Heinzi我想你的意思是,我再也不能在其他任何地方使用通用的IEntity了,比如在另一层(e.x业务层)。@ali:没错。除非你让
IEntity
实现一个通用的
IEntity
,否则你就回到对象上来了……谢谢你的回答。PolylineAdapter派生自BasePoly而非BasePoly。我说得对吗?如果是真的,那么我想上面会给出实体上的编译错误。@ali哎呀,我忘了在
BasePoly
中为
BasePoly
添加子类签名
PolyLineAdapter
继承自
BasePoly
,后者继承自
BasePoly
,因此,它应该可以正常工作。我在这个网站上写的代码,所以我没有花时间编译它。我只是想让大家明白我的想法,但忽略了一个关键点。对不起,我太挑剔了,但是如果我正确理解了逻辑,那么AutocadEntity应该同时实现Entity和Entity。正如我所设想的,正是IEntity在其他层上充当代理。是吗?@ali抱歉,我更新的时候已经很晚了,我错过了另一个关键点。我已经更新了答案,并提供了第二个选项。还有第三个适配器选项,但需要查看调用
IEntity.GetPoly()
IEntity.SetPoly()
方法的示例代码。@ali还注意到第二个选项中缺少泛型。在那里可能仍然有使用泛型的机会,但是在
AutocadEntity
SketchupEntity
类之间复制的任何代码都将被删除。
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 {}