多接口实现引起的混淆

3

我有以下一组接口和类。

public interface IValidatableObject
{
   List<string> ValidationErrors { get; }
   bool Validate();
}

public class ValidatableObject : IValidatableObject
{
   public List<string>ValidationErrors { get; }
   public bool Validate()
   {
      //dostuff
   }
}

public interface IDeviceDataObject
{
   int Id { get; set; }
   string Name { get; set; }
}

public class DeviceDataObject : ValidatableObject, IDeviceDataObject
{
   public int Id { get; set; }
   public string Name { get; set; }
}

public class DeviceService
{
   public bool ValidateDevice(IDeviceDataObject device)
   {
       return device.Validate(); // This throws a compiler error
   }
}

在上述服务操作ValidateDevice中的问题在于编译器无法解析device.Validate(),因为IDeviceDataObject未实现IValidatableObject接口。
我的问题是,将IValidatableObject更改为实现IValidatableObject是否正确。我有点不确定这是否是良好的实践,因为我认为DeviceDataObject通过ValidatableObject和IDeviceDataObject两次实现了IValidatableObject。请问是否有人能帮我澄清这一点?
public interface IDeviceDataObject : IValidatableObject
{
   int Id { get; set; }
   string Name { get; set; }
}

1
为什么你的ValidateDevice签名不像这样:public bool ValidateDevice(IValidatableObject someobj)?这不是表达验证方法功能的方式吗:它接受可验证的东西。其他所有东西都可以保持原样。 - Philip Daubmeier
公平的要求。但是如果需要知道传入的是IDeviceDataObject,但仍需要具有IValidatableObject功能的CreateDevice方法呢? - Chris
因为我不知道这里的大局,所有的IDeviceDataObjects是否都应该可验证?如果是的话,那么让IDeviceDataObject继承IValidatableObject。如果不是,那么这不是解决方案。有时候真的就是这么简单:只是语义上有意义的问题。将其转化为代码,反过来也是一个相当简单的步骤。 - Philip Daubmeier
4个回答

1

我可能在这里理解错了(而且我不知道你的整个类架构),但为什么你的ValidateDevice方法不只接受可验证对象呢?签名看起来应该是:

public bool ValidateDevice(IValidatableObject someobj)

这不就是表达一个执行验证功能的方法的功能吗:它接受一个可以验证的东西。其他所有内容都可以保持不变(即不要让IDeviceDataObject继承自IValidatableObject,因为您可能想要表达并非每个设备数据对象都可以进行验证,例如)

第二种方法,如果您想确保ValidateDevice仅接受实现IDeviceDataObject的对象,您还可以尝试交叉转换为IValidatableObject

public bool ValidateDevice(IDeviceDataObject someobj)
{
    if(someobj is IValidatableObject)
    {
        return ((IValidatableObject)device).Validate();
    }
    return //something that makes sense if the device is not validatable
}

1

你可以直接转换为 IValidatableObject。

public class DeviceService 
{ 
   public bool ValidateDevice(IDeviceDataObject device) 
   { 
       IValidatableObject v = device as IValidatableObject;

       if (v != null)
           return device.Validate();
       return false;
   } 
} 

但是,所有处于有效状态但不实现IValidatableObject的设备将无法通过验证。 - Eranga
@Eranga - 然后呢?他们通知有效的唯一方法是实现IValidatableObject。我不认为有什么问题。 - Erik Funkenbusch
API有误导。我本来期待ValidateDevice方法根据IDeviceDataObject的约定进行验证。要弄清楚我还需要实现IValidatableObject,只能通过查看ValidateDevice的具体实现。 - Eranga
@Eranga - 不,API 没有撒谎。如果对象没有实现 IValidatableObject,则无法验证该对象。就是这么简单。要解决问题,有一个叫做“文档”的东西。 - Erik Funkenbusch

0
你可以将你的 ValidateDevice 方法设置为泛型,让调用者传入实现了正确接口组合的对象实例 - 这将使你的接口保持独立并仍然强制类型安全性:
public class DeviceService
{
   public bool ValidateDevice<T>(T device) where T: IDeviceDataObject, IValidatableObject
   {
       return device.Validate(); 
   }
}

0

从名称中我推断了很多,但是在我看来,IDeviceDataObjectIValidatableObject是不同的概念。你可以有实现IDeviceDataObjectIValidatableObject或两者都实现的对象(对吗?)。如果是这样的话,那么两个接口之间就没有“is a”的关系(你不会认为IDeviceDataObject是一个IValidatableObject),所以从一个接口继承另一个接口似乎是错误的。

至于你的ValidateDevice方法(或任何方法)——如果它将参数用作IDeviceDataObject,那么参数应该是该类型。如果它将参数用作IValidatableObject,那么参数应该是那个类型。如果它可能使用两个功能,你可能想传递一个不太具体的类型,然后使用C#的'is'或'as'运行时检查来查看对象是否支持特定的接口。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接