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

Geo*_*pos 5 c# interface-implementation .net-core

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

我了解接口的基础知识,因为它们应该是实现它们的任何类的“契约”。接口中定义的任何内容都需要在继承(不确定这是否是正确的术语)它们的任何类中实现。

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

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

class ClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
    public int X { get; set; }
}
Run Code Online (Sandbox Code Playgroud)
interface IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

class ClassThatImplementsIAnInterface : IAnInterface
{
    public ClassThatImplementsIPropertyThatIsAnInterface InterfaceImplmentationProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

我这样做的原因是因为我想IPropertyThatIsAnInterfaceClassThatImplementsIAnInterface我创建的每个对象实现自定义。IE

class AnotherClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
   public int X { get; set; }
   public int Z { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

class ClassThatImplementsIAnInterface : IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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

那么,这是 C# 的限制还是我理解的限制?

The*_*aot 6

这是有效的 C#:

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

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

class ClassThatImplementsIAnInterface : IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

你可以这样做

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

    public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}

class ClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
    public int X { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

请注意,InterfaceImplmentationProperty与 无关IAnInterface

我们可以InterfaceProperty显式地实现implement,从而隐藏它:

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty { get; set; }
Run Code Online (Sandbox Code Playgroud)

然而,InterfacePropertyInterfaceImplmentationProperty仍然是分开的。让我们委托InterfacePropertyInterfaceImplmentationProperty

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
    get => InterfaceImplmentationProperty;
    set => InterfaceImplmentationProperty = value; // ERROR
}
Run Code Online (Sandbox Code Playgroud)

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

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

IPropertyThatIsAnInterface IAnInterface.InterfaceProperty
{
    get => InterfaceImplmentationProperty;
    set => InterfaceImplmentationProperty = (ClassThatImplementsIPropertyThatIsAnInterface)value;
}
Run Code Online (Sandbox Code Playgroud)

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

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

        InterfaceImplmentationProperty = valueAsSpecificType;
    }
}
Run Code Online (Sandbox Code Playgroud)

好吧,我们在上面看到我们必须改变接口的契约才能使其工作。那么...让合同更加灵活怎么样?

我们首先使接口变得通用:

interface IAnInterface<TPropertyThatIsAnInterface>
    where TPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
    TPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

在这里遵循您的命名风格。

那么我们在实现的时候就可以指定类型了:

class ClassThatImplementsIAnInterface : IAnInterface<ClassThatImplementsIPropertyThatIsAnInterface>
{
    public ClassThatImplementsIPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

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


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



来自评论:

我已实施您的建议,但在尝试填充List<IFileProcessors<IProcessorParameters>>. 我收到“无法隐式转换类型”错误。

看?您想将它们视为同一类型。然后使它们成为同一类型。

嗯,有一种模式,那就是有两个版本的接口,一个是通用的,另一个不是。那么泛型接口继承自非泛型。我想说的是,如果你能避免这种情况,就避免它。如果有的话,它将导致运行时更多的类型检查。


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

正如我在上面解释的那样,设置器是一个问题。

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

可以去掉设置器吗?

interface IAnInterface
{
    IPropertyThatIsAnInterface InterfaceProperty { get; }
}

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

class ClassThatImplementsIAnInterface : IAnInterface
{
    public IPropertyThatIsAnInterface InterfaceProperty { get; }
}
Run Code Online (Sandbox Code Playgroud)

ClassThatImplementsIAnInterface仍然能够使用InterfaceProperty任何实现IPropertyThatIsAnInterface. 消费者不必意识到这一点。并且,假设IPropertyThatIsAnInterface它确实有用(应该),则无需强制转换即可使用它。这取决于消费者是否可以处理该界面。当消费者需要某种特定类型的时候,你就会进行选角。