遗憾的是,接口成员上的 protected 并不按你预期的方式工作。
对于接口成员来说,这个修饰符有些特别,不过 C# 规范中提供了一些说明,至少 v8 草案 (https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/basic-concepts.md#754-protected-access) 中是这样:
访问级别为 protected 或 private protected 的接口实例成员,不能从实现该接口的类或结构中访问,只有从它派生的接口才能访问这些成员。
这个区别在接口成员的默认实现中有意义。例如下面这样,泛型参数在这里没有影响,所以我把它省略了:
public interface IReading {
public float Adjustment { get; protected set; }
}
public interface IExtendedReading : IReading {
public float AdvanceAdjustment() => Adjustment++;
}
在具体类中使用显式实现,可以避免公开 setter,比如使用自动实现的属性:
public class Reading : IReading {
float IReading.Adjustment { get; set; }
}
...或者完整实现访问器:
public class Reading : IReading {
private float adjustment;
float IReading.Adjustment {
get => adjustment;
set => adjustment = value;
}
}
这样 setter 就不会公开给其他类,但仍然没有实现你想要的行为。
在这里,抽象类可能比接口更合适。
从英语翻译·查看原文Unfortunately, an interface member marked protected doesn't behave the way you're expecting.
That modifier is somewhat unusual on interface members, although the C# specification (the draft v8 text (https://github.com/dotnet/csharpstandard/blob/draft-v8/standard/basic-concepts.md#754-protected-access), at least) offers some explanation:
Interface instance members whose accessibility is protected or private protected aren't accessible from its implementing classes or structs; only interfaces deriving from it may access them.
The distinction matters for default implementations of interface members. Here's an example (I've omitted the generic parameter because it doesn't affect this):
public interface IReading {
public float Adjustment { get; protected set; }
}
public interface IExtendedReading : IReading {
public float AdvanceAdjustment() => Adjustment++;
}
An explicit implementation in the concrete class lets you avoid exposing a publicly accessible setter, using either an auto-implemented property:
public class Reading : IReading {
float IReading.Adjustment { get; set; }
}
... or implementing the accessors in full:
public class Reading : IReading {
private float adjustment;
float IReading.Adjustment {
get => adjustment;
set => adjustment = value;
}
}
The setter then isn't exposed publicly to other classes, though this still doesn't achieve the behavior you're seeking.
An abstract class may be a better choice than an interface for this.