在界面中使用泛型
Using generic in the interface
更新:
海因齐是对的。 AutoCAD 多段线是引用类型而不是结构。好点子。但我已经简化了场景,因为我在实际应用程序中处理的是一个结构化的 AutoCAD 对象。所以请将两者都视为结构而不是引用类型。
我正在寻找应对这种情况的正确方法,如果有人能阐明或帮助我更好地理解,我将不胜感激。
数据访问层中有一个接口,有两个实现来处理两个不同的提供程序: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){...}
}
Autocad 的 GetPoly 实现将 return 折线对象,因为这是在 Autocad API 中定义为折线的对象,而 Sketchup 将 return 面对象。
我已经将 return 类型(和参数)定义为处理这些不同类型的对象。成本是 boxing/unboxing 发生的性能问题。在 return/parameter 是 object[] 的地方,它会更大胆地展示自己。
我首先想知道使方法 return/parameter 类型通用是解决方案,但后来我认为这不会,因为实现是特定于类型的。
The cost is performance issue where boxing/unboxing happens.
我不这么认为。 Polyline 是一个 class,不是结构。因此,不涉及拳击。
如果这实际上是您应用程序的性能瓶颈,那么您的性能会在其他地方丢失。一如既往:在优化之前进行测量,否则您最终可能会优化错误的东西。
我认为您的解决方案非常好。您 可以 使用泛型并从 IEntity<Polyline>
派生 AutocadEntity
,但有什么意义呢?由于 Polyline/Face 在您的界面中同时用作输入和输出参数,因此您可以使 IEntity
既不协变也不逆变。因此,IEntity<Polyline>
和 IEntity<Face>
最常见的基本类型是 object
,这意味着如果您不知道具体类型。
由于您有两个不同的 类 实现接口,我认为您最好的选择是使接口通用。
interface IEntity<T>
{
T GetPoly();
void InsertPoly(T poly);
}
class AutocadEntity : IEntity<Polyline>
{
Polyline GetPoly(){...}
void InsertPoly(Polyline poly) {...}
}
尝试使用 adapter pattern 将折线和面类型调整为您希望使用的单一类型。例如:
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 {}
使用 adapter pattern,您提供了一个代理层,使两个第三方类型符合您要使用的类型。
请记住,此设计假定您一次只会使用一种类型的第三方引擎。如果您将同时使用 两个 引擎,请进行以下更改:
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/FaceAdapter 或 AutocadEntity/SketchupEntity 类型的调用者的适配器模式本身.本质上,构建一个插件引擎。您也许能够使用泛型来抽象出两种实现之间的常见习语。
这是一个 dotnetfiddle 示例:https://dotnetfiddle.net/UsFPM7
更新:
海因齐是对的。 AutoCAD 多段线是引用类型而不是结构。好点子。但我已经简化了场景,因为我在实际应用程序中处理的是一个结构化的 AutoCAD 对象。所以请将两者都视为结构而不是引用类型。
我正在寻找应对这种情况的正确方法,如果有人能阐明或帮助我更好地理解,我将不胜感激。
数据访问层中有一个接口,有两个实现来处理两个不同的提供程序: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){...}
}
Autocad 的 GetPoly 实现将 return 折线对象,因为这是在 Autocad API 中定义为折线的对象,而 Sketchup 将 return 面对象。
我已经将 return 类型(和参数)定义为处理这些不同类型的对象。成本是 boxing/unboxing 发生的性能问题。在 return/parameter 是 object[] 的地方,它会更大胆地展示自己。
我首先想知道使方法 return/parameter 类型通用是解决方案,但后来我认为这不会,因为实现是特定于类型的。
The cost is performance issue where boxing/unboxing happens.
我不这么认为。 Polyline 是一个 class,不是结构。因此,不涉及拳击。
如果这实际上是您应用程序的性能瓶颈,那么您的性能会在其他地方丢失。一如既往:在优化之前进行测量,否则您最终可能会优化错误的东西。
我认为您的解决方案非常好。您 可以 使用泛型并从 IEntity<Polyline>
派生 AutocadEntity
,但有什么意义呢?由于 Polyline/Face 在您的界面中同时用作输入和输出参数,因此您可以使 IEntity
既不协变也不逆变。因此,IEntity<Polyline>
和 IEntity<Face>
最常见的基本类型是 object
,这意味着如果您不知道具体类型。
由于您有两个不同的 类 实现接口,我认为您最好的选择是使接口通用。
interface IEntity<T>
{
T GetPoly();
void InsertPoly(T poly);
}
class AutocadEntity : IEntity<Polyline>
{
Polyline GetPoly(){...}
void InsertPoly(Polyline poly) {...}
}
尝试使用 adapter pattern 将折线和面类型调整为您希望使用的单一类型。例如:
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 {}
使用 adapter pattern,您提供了一个代理层,使两个第三方类型符合您要使用的类型。
请记住,此设计假定您一次只会使用一种类型的第三方引擎。如果您将同时使用 两个 引擎,请进行以下更改:
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/FaceAdapter 或 AutocadEntity/SketchupEntity 类型的调用者的适配器模式本身.本质上,构建一个插件引擎。您也许能够使用泛型来抽象出两种实现之间的常见习语。
这是一个 dotnetfiddle 示例:https://dotnetfiddle.net/UsFPM7