将构造函数参数从接口类型强制转换为具体类类型是否错误

本文关键字:类型 错误 是否 转换 参数 构造函数 接口类型 | 更新日期: 2023-09-27 18:04:23

给定如下类:

public interface ISession {}
public class Session : ISession
{
    internal Uri ServerUri { get; private set; } // not in interface obviously
}

现在我在同一个项目中有另一个类,它依赖于Session:

public  interface IDataClass {}
internal DataClass : IDataClass
{
    private readonly Session _session;
    public DataClass(ISession session)
    {
        _session = (Session) session;
    }
    // some more methods that access the internal property _session.ServerUri
    // and call methods on the session object
    // ...
}

实际上Session类非常复杂。为了对DataClass进行单元测试,我将构造函数参数设置为ISession,因此我能够模拟依赖关系并验证ISession模拟上的某些方法调用。

然而,DataClass中的一些方法必须访问_session.ServerUri属性,因此字段必须是Session类型而不是ISession

我还可以使字段类型为ISession,并在每次访问内部属性时强制转换该字段,这将使在这个特定位置更清楚为什么需要具体的internal类。

每次我以上述方式实现强制类型转换时,我都想知道如果有更好的代码设计,是否可以避免强制类型转换。我经常找到更好的方法来摆脱石膏。

但是上面的例子呢?这是好的代码设计吗?还是有更干净的方法?我只是觉得用上面的方法从构造函数初始化字段很奇怪。

将构造函数参数从接口类型强制转换为具体类类型是否错误

老实说,我认为你目前的系统不是个好主意。

你的构造函数声明"只要它实现了ISession,我就接受任何东西",但这不是真的真的——调用代码在试图创建DataClass实例时得到InvalidCastException时,可能会发现这一点。

所以你真的应该在ISession 中声明ServerUriISession继承的接口-但在任何一种情况下,你需要声明(并存储)这个依赖关系仅在DataClass

你是对的,像这样的强制转换很容易出错,因为编译器没有简单的方法来告诉它会失败。

然而,看起来问题的根源是缺乏对ServerUri属性的访问,所以您添加了强制转换作为解决方案。如果是这种情况,您应该能够同时拥有它——保留接口,并且可以访问该属性,如果您可以添加一个新接口:

public interface ISessionWithServerUri : ISession { // Come up with a better name
    Uri ServerUri {get;}
}

现在您的Session类可以实现ISessionWithServerUri,允许您将_session的声明从Session类更改为ISessionWithServerUri接口:

internal DataClass : IDataClass
{
    private readonly ISessionWithServerUri _session;
    public DataClass(ISessionWithServerUri session)
    {
        _session = session; // Look, no cast!
    }
    // some more methods that access the internal property _session.ServerUri
    // and call methods on the session object
    // ...
}

由于DataClass依赖于Session,因此它应该在其构造函数中声明它。该类的任何客户端都应该能够从构造函数中定义的类型/契约创建一个有效的实例。

如果你想依赖于一个接口,那么你应该创建一个包含你需要的成员的新接口,例如

public interface IUriSession : ISession
{
   Uri ServerUri { get; }
}
并将构造函数更改为
public DataClass(IUriSession session) { ... }

我会尽量避免像在构造函数中那样的显式类型强制转换。我假设您正在使用IoC容器来解析依赖关系,如果具体类型映射更改为实现该接口的不同类(并且没有该内部属性),这显然会中断。这也有点违背了在该类中使用依赖注入的目的。

你能改变数据类的构造函数注入不同的类型吗?如果是这样,您可以创建一个实现了ISession的接口,并且还包含了ServerUri属性,创建一个实现了新接口的类,然后将该类型注入到构造函数(接口)中。