具有接口属性的C#接口实现

时间:2019-11-28 01:11:39

标签: c# .net-core interface-implementation

我将开始更深入地研究C#编程,并且一直在弄乱接口。

我理解接口的基础,因为它们应该是实现它们的任何类的“合同”。接口中定义的任何内容都需要在从它们继承的任何类中实现(不确定是否正确)。

因此,我向接口添加了一个属性,该属性本身就是一个接口。尝试实现父接口时,我向类添加了一个属性,该类是实现作为父接口中的属性的接口的类。这种解释可能有点混乱,所以我在下面添加了一些代码。

interface IPropertyThatIsAnInterface
{
    public int X { get; set; }
}

class ClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
    public int X { get; set; }
}
interface IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

class ClassThatImplementsIAnInterface : IAnInterface
{
    public ClassThatImplementsIPropertyThatIsAnInterface InterfaceImplmentationProperty { get; set; }
}

Visual Studio(在.Net Core 3.0 [即C#8.0]中)出现错误,指出我的类ClassThatImplementsIAnInterface没有实现接口属性public IPropertyThatIsAnInterface InterfaceProperty { get; set; }(接口属性)。这是C#的限制还是我对接口的理解的限制?

之所以这样做,是因为我想为我创建的每个IPropertyThatIsAnInterface实现自定义的ClassThatImplementsIAnInterface。即

class AnotherClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
   public int X { get; set; }
   public int Z { get; set; }
}

我可以通过以下操作解决此问题

class ClassThatImplementsIAnInterface : IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

但是问题是,当我访问自定义类属性之一时,访问该属性时,我需要在类定义中将对InterfaceProperty的每次调用都强制转换为((ClassThatImplementsIPropertyThatIsAnInterface)InterfaceProperty).Y。可以,但是不太好。

再说一次,这是C#限制还是我的理解限制?

2 个答案:

答案 0 :(得分:0)

首先,属性名称必须为InterfaceProperty,而不是InterfaceImplmentationProperty-属性名称必须匹配。

第二,是的,类型必须完全匹配。但是,这并不能阻止您将ClassThatImplementsIPropertyThatIsAnInterface对象分配给InterfaceProperty

但是,如果您说必须在使用它之前进行转换,那么这意味着该接口不适合您的工作。 interface的全部要点是,对象的使用者可以使用接口的功能,而不必知道它们的实现方式。

此处正确的术语是您“实现”接口。使用接口是一种“可以做”的类型关系。例如,实现IDisposable“可以被处置”的类。一个类可以实现多个接口,通常可以这样做。例如,SqlConnection同时实现ICloneableIDisposable,这意味着它“可以被克隆”和“可以被处置”。

但是,一个类只能“继承”另一个类(abstract或具体的)。这是一种“是”关系。例如,SqlConnection“是DbConnection”,如您在声明中看到的那样:

public sealed class SqlConnection : System.Data.Common.DbConnection, ICloneable, IDisposable

这里对此有一些更好的阅读:Interfaces (C# Programming Guide)

在这里:Abstract and Sealed Classes and Class Members (C# Programming Guide)

在这里:Classes (C# Programming Guide) - Class inheritance

这是我刚刚编造的一个奇怪的例子:

interface ICanMakeNoise {
    void MakeNoise();
}

abstract class Animal {
    public abstract int NumberOfLegs { get; }
}

class Dog : Animal, ICanMakeNoise {

    public override int NumberOfLegs { get; } = 4;

    public void MakeNoise() {
        // Bark
    }
}

class Radio : ICanMakeNoise {
    public void MakeNoise() {
        // Play music
    }
}

然后我可以有一个像这样的方法:

public void MakeThisMakeNoise(ICanMakeNoise noisyThing) {
    // This could be a dog or a radio, I don't have to know
    noisyThing.MakeNoise();
}

答案 1 :(得分:0)

这是有效的C#:

interface IAnInterface
{
    IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

interface IPropertyThatIsAnInterface
{
    int X { get; set; }
}

class ClassThatImplementsIAnInterface : IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

您可以这样做

class ClassThatImplementsIAnInterface : IAnInterface
{
    public ClassThatImplementsIPropertyThatIsAnInterface InterfaceImplmentationProperty { get; set; }

    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

class ClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
    public int X { get; set; }
}

请注意,InterfaceImplmentationPropertyIAnInterface无关。

我们可以显式地实现InterfaceProperty工具,将其隐藏起来:

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty { get; set; }

但是,InterfacePropertyInterfaceImplmentationProperty仍然是分开的。让我们将InterfaceProperty委派给InterfaceImplmentationProperty

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
    get => InterfaceImplmentationProperty;
    set => InterfaceImplmentationProperty = value; // ERROR
}

现在,我们有一个错误。因为,您会看到IPropertyThatIsAnInterface不一定是InterfaceImplmentationProperty。但是,IAnInterface承诺我可以在那里设置任何IPropertyThatIsAnInterface

如果继续这条路,我们就必须颠覆期望,并抛出异常:

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
    get => InterfaceImplmentationProperty;
    set => InterfaceImplmentationProperty = (ClassThatImplementsIPropertyThatIsAnInterface)value;
}

在这里,我添加了一个强制转换,这可能在运行时失败。可能很容易忽略...我们可以更具表现力:

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
    get => InterfaceImplmentationProperty;
    set
    {
        if (!(value is ClassThatImplementsIPropertyThatIsAnInterface valueAsSpecificType))
        {
            throw new ArgumentException($"{nameof(value)} is not {typeof(ClassThatImplementsIPropertyThatIsAnInterface)}", nameof(value));
        }

        InterfaceImplmentationProperty = valueAsSpecificType;
    }
}

好的,我们在上面看到,我们必须弯曲接口的约束才能使其正常工作。那么...如何使合同更加灵活?

我们首先使接口通用:

interface IAnInterface<TPropertyThatIsAnInterface>
    where TPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
    TPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

只需在这里遵循您的命名方式即可。

然后我们可以在实现它时指定类型:

class ClassThatImplementsIAnInterface : IAnInterface<ClassThatImplementsIPropertyThatIsAnInterface>
{
    public ClassThatImplementsIPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

如果您确实想要更丑陋的名称,则可以执行显式实现和委托属性。


更进一步...接口的作用是什么?是不是这样,您可以通过接口处理对象,而不必处理特定类型? -理想情况下,您不必强制转换或检查类型。



来自评论:

  

我已经执行了您的建议,但是在尝试填充List<IFileProcessors<IProcessorParameters>>时遇到问题。我收到“无法隐式转换类型”错误。

看到了吗?您想将它们视为相同类型。然后使它们具有相同的类型。

嗯,有一种模式可以实现,它有两个版本的接口,一个是通用的,而没有。然后,通用接口从非通用接口继承。我会说,如果您可以避免,那就避免。如果有的话,它将导致在运行时进行更多类型检查。


理想情况下,您应该能够通过其接口处理该类型。接口应该足够了。因此不需要特定的类型,因此也不需要强制转换。

就像我在二传手上面解释的那样。

如果属性的实际类型比接口上的类型更具体,则接口会说该属性允许该类所不允许的类型。

您可以删除二传手吗?

interface IAnInterface
{
    IPropertyThatIsAnInterface InterfaceProperty { get; }
}

interface IPropertyThatIsAnInterface
{
    int X { get; set; }
}

class ClassThatImplementsIAnInterface : IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; }
}

ClassThatImplementsIAnInterface仍然可以使用实现InterfaceProperty的任何类型来初始化IPropertyThatIsAnInterface。消费者不必知道这一点。并且,假设IPropertyThatIsAnInterface很有用(应如此)(应该),因此无需强制转换即可使用它。取决于消费者是否可以处理界面。当消费者需要特定类型的商品时,您将进行投射。