C#:方法返回的对象,其具体类型在运行时确定的? [英] C#: Method to return object whose concrete type is determined at runtime?

查看:186
本文介绍了C#:方法返回的对象,其具体类型在运行时确定的?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我在想设计一个会返回一个实现一个接口,但其具体类型不会知道,直到运行时对象的方法。例如,假设:

I'm thinking about designing a method that would return an object that implements an interface but whose concrete type won't be know until run-time. For example suppose:

ICar
Ford implements ICar
Bmw implements ICar
Toyota implements ICar

public ICar GetCarByPerson(int personId)

我们不知道是什么车我们会尽快回复到运行时。

We don't know what car we will get back until runtime.

A)我想知道这个人有什么类型的车。

a) I want to know what type of car the person has.

二)根据具体车型我们回来,我们将调用不同的方法(因为一些方法只能用于该类意义)。所以客户端代码会做这样的事情。

b) depending on the concrete car type we get back we will call different methods (because some methods only make sense on the class). So the client code will do something like.

ICar car = GetCarByPerson(personId);

if ( car is Bmw )
{
  ((Bmw)car).BmwSpecificMethod();
}
else if (car is Toyota)
{
  ((Toyota)car).ToyotaSpecificMethod();
}

这是一个好的设计?是否有一个代码味道?有没有更好的方式来做到这一点?

Is this a good design? Is there a code smell? Is there a better way to do this?

我很好,返回接口中的方法,如果客户端代码调用接口的方法显然,这将是罚款。但我关心的是客户端代码转换为具体的类型是否是不错的设计。

I'm fine with the method that returns the interface, and if the client code was calling interface methods obviously this would be fine. But my concern is whether the client code casting to concrete types is good design.

推荐答案

使用是在C#关键字(的方式你已经证明了以上)几乎总是一个代码味道。它太臭。

Using the is keyword in C# (in the manner you have demonstrated above) is almost always a code smell. And it stinks.

的问题是,什么是应该只知道一个 ICAR 现在需要保持实现几种不同类别的轨道 ICAR 。虽然这工作(在它产生的操作代码),它是设计不良。你会只用几辆车...

The problem is that something which is supposed to only know about an ICar is now required to keep track of several different classes that implement ICar. While this works (as in it produces code that operates), it's poor design. You're going to start off with just a couple cars...

class Driver
{
    private ICar car = GetCarFromGarage();

    public void FloorIt()
    {
        if (this.car is Bmw)
        {
            ((Bmw)this.car).AccelerateReallyFast();
        }
        else if (this.car is Toyota)
        {
            ((Toyota)this.car).StickAccelerator();
        }
        else
        {
            this.car.Go();
        }
    }
}

和以后,另一个的车会做一些特别的东西,当你 FloorIt 。你会添加功能驱动程序,你会想想需要处理其他的特殊情况下,你会浪费20分钟每一个地方追踪有一个如果(车美孚),因为它是分散在整个代码库现在 - 驱动程序里面 ,里面的车库,里面的停车场 ... <子>(我是从经验上遗留代码工作讲在这里。)

And later on, another car is going to do something special when you FloorIt. And you'll add that feature to Driver, and you'll think about the other special cases that need to be handled, and you'll waste twenty minutes tracking down every place that there is a if (car is Foo), since it's scattered all over the code base now -- inside Driver, inside Garage, inside ParkingLot... (I'm speaking from experience in working on legacy code here.)

当你发现自己做一个声明像如果(实例SomeObject),停下来问自己,为什么这个特殊的行为,这里需要处理。在大多数情况下,它可以在接口/抽象类的新方法,你可以简单地提供不属于特殊的类的默认实现。

When you find yourself making a statement like if (instance is SomeObject), stop and ask yourself why this special behavior needs to be handled here. Most of the time, it can be a new method in the interface/abstract class, and you can simply provide a default implementation for the classes that aren't "special".

这并不是说,你应该绝对不会检查类型的;但是,你必须在这种做法非常小心,因为它有可能失控,成为被滥用,除非保持在检查的倾向。

That's not to say that you should absolutely never check types with is; however, you must be very careful in this practice because it has a tendency to get out of hand and become abused unless kept in check.

现在,假设你已经确定你确凿必须键入检查你的 ICAR 。使用问题是是静态代码分析工具会警告你投了两次,当你做

Now, suppose you have determined that you conclusively must type-check your ICar. The problem with using is is that static code analysis tools will warn you about casting twice, when you do

if (car is Bmw)
{
   ((Bmw)car).ShiftLanesWithoutATurnSignal();
}



对性能的影响可能是微不足道的,除非它是在一个内部循环,但首选写这个的办法就是

The performance hit is probably negligible unless it's in an inner loop, but the preferred way of writing this is

var bmw = car as Bmw;
if (bmw != null) // careful about overloaded == here
{
    bmw.ParkInThreeSpotsAtOnce();
}

这仅需要一个投(内部),而不是两个。

This requires only one cast (internally) instead of two.

如果你不想去的路线,另一块干净的方法是简单地用一个枚举:

If you don't want to go that route, another clean approach is to simply use an enumeration:

enum CarType
{
    Bmw,
    Toyota,
    Kia
}

interface ICar
{
    void Go();

    CarType Make
    {
        get;
    }
}



其次

followed by

if (car.Make == CarType.Kia)
{
   ((Kia)car).TalkOnCellPhoneAndGoFifteenUnderSpeedLimit();
}

您可以快速开关在一个枚举,它可以让你知道(在某种程度上)什么可能被用于汽车的具体限制。

You can quickly switch on an enum, and it lets you know (to some extent) the concrete limit of what cars might be used.

一个缺点,使用枚举是 CarType 设在石;如果另一个(外部)组装取决于 ICAR 和他们增加了新的特斯拉汽车,他们将不能够添加特斯拉键入 CarType 。枚举也不能够很好的类层次:如果你想有一个雪佛兰是一个 CarType.Chevy 的一个 CarType.GM ,你要么必须使用枚举的标志(在这种情况下丑陋的),或确保您检查雪佛兰通用汽车,还是有很多的 || 在你对检查该枚举。

One downside to using an enum is that CarType is set in stone; if another (external) assembly depends on ICar and they added the new Tesla car, they won't be able to add a Tesla type to CarType. Enums also don't lend themselves well to class hierarchies: if you want a Chevy to be a CarType.Chevy and a CarType.GM, you either have to use the enum as flags (ugly in this case) or make sure you check for Chevy before GM, or have lots of ||s in your checks against the enums.

这篇关于C#:方法返回的对象,其具体类型在运行时确定的?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆