C# 将实际对象类型发送到重载函数 请考虑以下情况
我有一个类型为“Shapes”的超级类和继承形状的类“Box”、“Circle”和“Arrow”。我在其他地方有一个形状列表,可以包含这些类型的任何成员。我需要列举清单并画出每个形状。问题是每个形状的绘制方式不同,因此我有:C# 将实际对象类型发送到重载函数 请考虑以下情况,c#,inheritance,polymorphism,type-conversion,C#,Inheritance,Polymorphism,Type Conversion,我有一个类型为“Shapes”的超级类和继承形状的类“Box”、“Circle”和“Arrow”。我在其他地方有一个形状列表,可以包含这些类型的任何成员。我需要列举清单并画出每个形状。问题是每个形状的绘制方式不同,因此我有: void Draw(Box b) {} void Draw(Square s) {} void Draw(Circle c) {} 问题是,当我枚举列表时,返回的每个元素都是Shape类型(因为列表是Shape类型),即使它的实际类型可能是Box。因此,没有一个重载被
void Draw(Box b) {}
void Draw(Square s) {}
void Draw(Circle c) {}
问题是,当我枚举列表时,返回的每个元素都是Shape类型(因为列表是Shape类型),即使它的实际类型可能是Box。因此,没有一个重载被认为是正确的
我的一个想法是创建一个临时对象,并将其声明为列表元素的实际类型。所以,让我们假设列表[i]是圆的类型
object o = Type.GetType(Convert.ToString(list[i].GetType()));
o = list[i];
但这仍然不起作用,因为现在编译器将“o”的类型识别为Object而不是Circle
我怎样才能避开这个问题
void Draw(Shapes s)
{
if (s is Circle)
{
Draw((Circle)s);
}
}
依此类推。在
Shape
类中创建一个抽象的Draw
方法。在每个派生类中重写它。让特定的形状自己画
例如:
public interface IDrawingSurface {
// All your favorite graphics primitives
}
public abstract class Shape {
public abstract void Draw();
protected IDrawingSurface Surface {get;set;}
public Shape(IDrawingSurface surface) {
Surface = surface;
}
}
public class Box {
public Box(IDrawingSurface surface) : base(surface) {}
public virtual void Draw(){ Surface.Something();... }
}
public class Square {
public Square(IDrawingSurface surface) : base(surface) {}
public virtual void Draw(){ Surface.Something();... }
}
public class Circle {
public Circle(IDrawingSurface surface) : base(surface) {}
public virtual void Draw(){ Surface.Something();... }
}
更好的解决方案是在shape
Draw()
上使用abstract
函数,然后在Box、Circle和Sqaure中重写该函数。为什么不在shape上使用Draw虚拟方法,然后在每个派生类上重写它呢?然后你可以这样做:
List<Shapes> shapes = ... get your list of shapes somewhere ...
foreach (var shape in shapes) {
shape.Draw();
}
列表形状=。。。在某处获取您的形状列表。。。
foreach(形状中的变量形状){
shape.Draw();
}
将对特定派生类调用正确的虚方法。这是多态性的教科书案例 公共接口IShape
public interface IShape
{
public void Draw();
}
public class Circle : IShape
{
public void Draw()
{
do somethig shape specific
}
}
List<IShape> shapes
foreach (IShape shape in shapes)
{
shape.Draw();
}
{
公众提款();
}
公共阶级圈子:IShape
{
公众抽签()
{
做一些特定形状的事情
}
}
列表形状
foreach(形状中的IShape形状)
{
shape.Draw();
}
我个人会按照建议使用一个接口,但如果您无法访问呼叫您的用户(如第三方.dll),则可以接受object类型的参数:
/// <summary>
/// Draw any type of object if the objec type is supported.
/// Circles, Squares, etc.
/// </summary>
/// <param name="objectToDraw"></param>
public void Draw(object objectToDraw)
{
// get the type of object
string type = objectToDraw.GetType().ToString();
switch(type)
{
case "Circle":
// cast the objectToDraw as a Circle
Circle circle = objectToDraw as Circle;
// if the cast was successful
if (circle != null)
{
// draw the circle
circle.Draw();
}
// required
break;
case "Square":
// cast the object as a square
Square square = objectToDraw as Square;
// if the square exists
if (square != null)
{
// draw the square
square.Draw();
}
// required
break;
default:
// raise an error
throw new Exception("Object Type Not Supported in Draw method");
}
}
//
///如果支持objec类型,则绘制任何类型的对象。
///圆、正方形等。
///
///
公共无效绘图(对象objectToDraw)
{
//获取对象的类型
字符串类型=objectToDraw.GetType().ToString();
开关(类型)
{
案例“圆圈”:
//将objectToDraw投射为一个圆
圆圈=对象绘制为圆圈;
//如果演员阵容成功
如果(圆圈!=null)
{
//画圆圈
画圆();
}
//必需的
打破
案例“方形”:
//将对象投射为正方形
Square Square=对象绘制为正方形;
//如果正方形存在
如果(平方!=null)
{
//画正方形
正方形。画();
}
//必需的
打破
违约:
//出错
抛出新异常(“Draw方法中不支持对象类型”);
}
}
在Box类上绘制的方式是错误的。Box类应该只有Draw()方法
若要使用“图形类型列表”进行绘图,则必须确保已在派生类中重写Draw方法
您标记为已接受的答案详细说明了这一点
另外,为了获取信息,绘制(框b)适合静态方法类别。+1。或者使用一个接口,比如
IDrawable
,并让可以绘制的子类实现该接口。这是我的第二个想法,它肯定会起作用。我最恼火的是,我创建的任何控件都必须传入对pictureBox的引用才能绘制。我希望找到一种摆脱它的方法。将PictureBox推广到IDrawingSurface
界面,那么至少你不会依赖于特定的绘图技术。您还可以将该接口传递到形状的构造函数中,这样就不需要在每次Draw
调用中传递该接口。由其他形状组成的形状可以将其自身的IDrawingSurface
引用传递给组件形状,也就是说,“绘制到我绘制到的同一位置”。虽然可以这样做,但这比我在之前的回答中建议的虚拟方法要昂贵得多。这里的方法需要将反射分派到正确的方法,这会很慢。当然,但如果OP不想更改其形状,这是另一种方法。谢谢,这绝对是一种解决方案,但我避免了通过if-else阶梯来找出对象的实际类型。我觉得这有点否定了多态性的全部观点,不是吗?
void Draw(Box b) {}