带接口的 C# 接口实现 属性
C# interface implementation with an interface property
我开始更深入地研究 C# 编程,并且一直在研究接口。
我了解接口的基础知识,因为对于实现它们的任何 class,它们都应该是 "contracts"。接口中定义的任何内容都需要在从它们继承(不确定该术语是否正确)的任何 class 中实现。
所以我加了一个属性到一个本身就是接口的接口。当尝试实现父接口时,我在我的 class 中添加了一个 属性,这是一个 class,它实现了父接口中的 属性 接口。这个解释可能有点令人困惑,所以我在下面添加了一些代码。
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] 中)出现错误,指出我的 class、ClassThatImplementsIAnInterface
未实现接口 属性, public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
, (这是一个接口 属性)。这是C#的局限还是我对接口理解的局限?
我这样做的原因是因为我想为我创建的每个 ClassThatImplementsIAnInterface
实现定制的 IPropertyThatIsAnInterface
。 IE。
class AnotherClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
public int X { get; set; }
public int Z { get; set; }
}
我可以通过执行以下操作来解决这个问题
class ClassThatImplementsIAnInterface : IAnInterface
{
public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
但问题在于,当我访问 属性 时,我需要在我的 class 定义中对 InterfaceProperty
进行每次调用,即 ((ClassThatImplementsIPropertyThatIsAnInterface)InterfaceProperty).Y
每当我访问一个自定义 class 属性。这还可以,但不是很好。
那么,这是 C# 的限制还是我理解的限制?
首先,属性 名称必须是 InterfaceProperty
,而不是 InterfaceImplmentationProperty
- 属性 名称必须匹配。
其次,是的,类型必须完全匹配。但是,这不会阻止您将 ClassThatImplementsIPropertyThatIsAnInterface
对象分配给 InterfaceProperty
.
但是,如果您说在使用它之前必须进行转换,那么这意味着该界面不适合您正在做的事情。 interface 的全部要点是对象的使用者可以使用接口的功能,而不必知道它们是如何实现的。
此处正确的术语是您 "implement" 界面。使用接口是 "can do" 类型的关系。例如,实现 IDisposable
"can be disposed". A class can implement several interfaces, and that is commonly done. For example, SqlConnection
的 class 同时实现 ICloneable
和 IDisposable
,这意味着它 "can be cloned" 和 "can be disposed".
但是,一个 class 只能 "inherit" 另一个 class(或者 abstract
or concrete). This is a "is a" relationship. For example, SqlConnection
"is a 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();
}
这是有效的 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; }
}
请注意InterfaceImplmentationProperty
与IAnInterface
无关。
我们可以显式地实现InterfaceProperty
,隐藏它:
IPropertyThatIsAnInterface IAnInterface.InterfaceProperty { get; set; }
然而,InterfaceProperty
和 InterfaceImplmentationProperty
仍然是分开的。让我们将 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; }
}
如果你真的想要丑陋的名字,你可以做显式实现和委托 属性 事情。
更进一步...界面的用途是什么?是不是这样您就可以通过接口处理对象而不必处理特定类型? - 理想情况下,您不必强制转换或检查类型。
来自评论:
I have implemented your suggestion but am having an issue when trying to populate a List<IFileProcessors<IProcessorParameters>>
. I get a "cannot implicitly convert type" error.
看到了吗?您希望将它们视为同一类型。然后让他们成为同一类型。
好吧,有一个模式,就是有两个版本的界面,一个是通用的,另一个不是。然后泛型接口继承自非泛型。我会说,如果你能避免它,就避免它。如果有的话,它将导致在运行时进行更多类型检查。
理想情况下,您应该能够通过其接口处理该类型。界面应该够用了。因此不需要特定类型,因此也不需要使用它。
正如我在上面解释的那样 setter 是个问题。
如果 属性 的实际类型比界面上的类型更具体,那么界面会说 属性 允许 class 不允许的类型。
你能删除 setter 吗?
interface IAnInterface
{
IPropertyThatIsAnInterface InterfaceProperty { get; }
}
interface IPropertyThatIsAnInterface
{
int X { get; set; }
}
class ClassThatImplementsIAnInterface : IAnInterface
{
public IPropertyThatIsAnInterface InterfaceProperty { get; }
}
ClassThatImplementsIAnInterface
仍然可以使用任何实现 IPropertyThatIsAnInterface
的类型来初始化 InterfaceProperty
。消费者不必意识到这一点。而且,假设 IPropertyThatIsAnInterface
是有用的(它应该),应该不需要转换它来使用它。取决于消费者是否可以处理界面。在消费者需要特定类型的那一刻,您将进行铸造。
我开始更深入地研究 C# 编程,并且一直在研究接口。
我了解接口的基础知识,因为对于实现它们的任何 class,它们都应该是 "contracts"。接口中定义的任何内容都需要在从它们继承(不确定该术语是否正确)的任何 class 中实现。
所以我加了一个属性到一个本身就是接口的接口。当尝试实现父接口时,我在我的 class 中添加了一个 属性,这是一个 class,它实现了父接口中的 属性 接口。这个解释可能有点令人困惑,所以我在下面添加了一些代码。
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] 中)出现错误,指出我的 class、ClassThatImplementsIAnInterface
未实现接口 属性, public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
, (这是一个接口 属性)。这是C#的局限还是我对接口理解的局限?
我这样做的原因是因为我想为我创建的每个 ClassThatImplementsIAnInterface
实现定制的 IPropertyThatIsAnInterface
。 IE。
class AnotherClassThatImplementsIPropertyThatIsAnInterface : IPropertyThatIsAnInterface
{
public int X { get; set; }
public int Z { get; set; }
}
我可以通过执行以下操作来解决这个问题
class ClassThatImplementsIAnInterface : IAnInterface
{
public IPropertyThatIsAnInterface InterfaceProperty { get; set; }
}
但问题在于,当我访问 属性 时,我需要在我的 class 定义中对 InterfaceProperty
进行每次调用,即 ((ClassThatImplementsIPropertyThatIsAnInterface)InterfaceProperty).Y
每当我访问一个自定义 class 属性。这还可以,但不是很好。
那么,这是 C# 的限制还是我理解的限制?
首先,属性 名称必须是 InterfaceProperty
,而不是 InterfaceImplmentationProperty
- 属性 名称必须匹配。
其次,是的,类型必须完全匹配。但是,这不会阻止您将 ClassThatImplementsIPropertyThatIsAnInterface
对象分配给 InterfaceProperty
.
但是,如果您说在使用它之前必须进行转换,那么这意味着该界面不适合您正在做的事情。 interface 的全部要点是对象的使用者可以使用接口的功能,而不必知道它们是如何实现的。
此处正确的术语是您 "implement" 界面。使用接口是 "can do" 类型的关系。例如,实现 IDisposable
"can be disposed". A class can implement several interfaces, and that is commonly done. For example, SqlConnection
的 class 同时实现 ICloneable
和 IDisposable
,这意味着它 "can be cloned" 和 "can be disposed".
但是,一个 class 只能 "inherit" 另一个 class(或者 abstract
or concrete). This is a "is a" relationship. For example, SqlConnection
"is a 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();
}
这是有效的 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; }
}
请注意InterfaceImplmentationProperty
与IAnInterface
无关。
我们可以显式地实现InterfaceProperty
,隐藏它:
IPropertyThatIsAnInterface IAnInterface.InterfaceProperty { get; set; }
然而,InterfaceProperty
和 InterfaceImplmentationProperty
仍然是分开的。让我们将 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; }
}
如果你真的想要丑陋的名字,你可以做显式实现和委托 属性 事情。
更进一步...界面的用途是什么?是不是这样您就可以通过接口处理对象而不必处理特定类型? - 理想情况下,您不必强制转换或检查类型。
来自评论:
I have implemented your suggestion but am having an issue when trying to populate a
List<IFileProcessors<IProcessorParameters>>
. I get a "cannot implicitly convert type" error.
看到了吗?您希望将它们视为同一类型。然后让他们成为同一类型。
好吧,有一个模式,就是有两个版本的界面,一个是通用的,另一个不是。然后泛型接口继承自非泛型。我会说,如果你能避免它,就避免它。如果有的话,它将导致在运行时进行更多类型检查。
理想情况下,您应该能够通过其接口处理该类型。界面应该够用了。因此不需要特定类型,因此也不需要使用它。
正如我在上面解释的那样 setter 是个问题。
如果 属性 的实际类型比界面上的类型更具体,那么界面会说 属性 允许 class 不允许的类型。
你能删除 setter 吗?
interface IAnInterface
{
IPropertyThatIsAnInterface InterfaceProperty { get; }
}
interface IPropertyThatIsAnInterface
{
int X { get; set; }
}
class ClassThatImplementsIAnInterface : IAnInterface
{
public IPropertyThatIsAnInterface InterfaceProperty { get; }
}
ClassThatImplementsIAnInterface
仍然可以使用任何实现 IPropertyThatIsAnInterface
的类型来初始化 InterfaceProperty
。消费者不必意识到这一点。而且,假设 IPropertyThatIsAnInterface
是有用的(它应该),应该不需要转换它来使用它。取决于消费者是否可以处理界面。在消费者需要特定类型的那一刻,您将进行铸造。