我将开始更深入地研究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#限制还是我的理解限制?
答案 0 :(得分:0)
首先,属性名称必须为InterfaceProperty
,而不是InterfaceImplmentationProperty
-属性名称必须匹配。
第二,是的,类型必须完全匹配。但是,这并不能阻止您将ClassThatImplementsIPropertyThatIsAnInterface
对象分配给InterfaceProperty
。
但是,如果您说必须在使用它之前进行转换,那么这意味着该接口不适合您的工作。 interface的全部要点是,对象的使用者可以使用接口的功能,而不必知道它们的实现方式。
此处正确的术语是您“实现”接口。使用接口是一种“可以做”的类型关系。例如,实现IDisposable
“可以被处置”的类。一个类可以实现多个接口,通常可以这样做。例如,SqlConnection
同时实现ICloneable
和IDisposable
,这意味着它“可以被克隆”和“可以被处置”。
但是,一个类只能“继承”另一个类(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; }
}
请注意,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; }
}
如果您确实想要更丑陋的名称,则可以执行显式实现和委托属性。
更进一步...接口的作用是什么?是不是这样,您可以通过接口处理对象,而不必处理特定类型? -理想情况下,您不必强制转换或检查类型。
来自评论:
我已经执行了您的建议,但是在尝试填充
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
很有用(应如此)(应该),因此无需强制转换即可使用它。取决于消费者是否可以处理界面。当消费者需要特定类型的商品时,您将进行投射。