C#8接口默认值:如何以良好可用的方式实现默认属性
我真的很喜欢在C#8中的接口上实现默认实现的想法。但是,经过尝试之后,失望很大...
所以这是一个简单的例子,我找到了一个答案的一部分, c# 8与其中定义的属性/方法的接口 - 显然已经不起作用为什么这是为什么:
public interface IHasFirstNames
{
string? FirstName => FirstNames.FirstOrDefault();
List<string> FirstNames { get; }
}
public class Monster : IHasFirstNames
{
public List<string> FirstNames { get; } = new();
public Monster(params string[] firstNames)
{
FirstNames.AddRange(firstNames);
}
}
public static class Program
{
public static void Main()
{
var sally = new Monster("James", "Patrick");
var error = sally.FirstName; // Cannot resolve symbol FirstName
var works = ((IHasFirstNames)sally).FirstName;
}
}
但是,如果您始终必须将其投射丑陋,那么在接口中默认实现属性的意义是什么?
因此,根据上面的铸造解决方案,我尝试了以下操作:
public interface IHasFirstNames
{
string? FirstName => FirstNames.FirstOrDefault();
List<string> FirstNames { get; }
}
public class Monster : IHasFirstNames
{
// Not ideal to declare the property here
// But at least the implementation is still in the interface
public string? FirstName => ((IHasFirstNames)this).FirstName;
public List<string> FirstNames { get; } = new();
public Monster(params string[] firstNames)
{
FirstNames.AddRange(firstNames);
}
}
public static class Program
{
public static void Main()
{
var sally = new Monster("James", "Patrick");
var error = sally.FirstName; // StackOverflow!
}
}
但是,由于铸件对ihasfirstname
,这会导致堆栈溢出,并没有真正称其为接口的默认实现。 即使我实现了一个具有专用变量ihasfirstname
的完整getter,它也会导致堆栈溢出。
我想出的唯一丑陋的解决方案是使用专用的Getter方法:
public interface IHasFirstNames
{
// Default implementation of a property is no use to me!
string? FirstName { get; }
// So I have to implement a getter method as default
public string? FirstNameGetter() => FirstNames.FirstOrDefault();
List<string> FirstNames { get; }
}
public class Monster : IHasFirstNames
{
public string? FirstName => ((IHasFirstNames)this).FirstNameGetter();
public List<string> FirstNames { get; } = new();
public Monster(params string[] firstNames)
{
FirstNames.AddRange(firstNames);
}
}
public static class Program
{
public static void Main()
{
var sally = new Monster("James", "Patrick");
var works= sally.FirstName;
}
}
它不一定是一种方法。显然,如果它具有不同名称的属性,也可以。一旦界面中的属性和班级中的属性应具有相同的名称,它就会变得丑陋。
真的没有更好的解决方案吗?
谢谢
I really loved the idea of default implementations on interfaces in C#8. But after trying it the disappointment was big...
So here's a simple example which I've found a part of the answer to in C#8 interfaces with properties/methods defined in them - apparently not working already why this is:
public interface IHasFirstNames
{
string? FirstName => FirstNames.FirstOrDefault();
List<string> FirstNames { get; }
}
public class Monster : IHasFirstNames
{
public List<string> FirstNames { get; } = new();
public Monster(params string[] firstNames)
{
FirstNames.AddRange(firstNames);
}
}
public static class Program
{
public static void Main()
{
var sally = new Monster("James", "Patrick");
var error = sally.FirstName; // Cannot resolve symbol FirstName
var works = ((IHasFirstNames)sally).FirstName;
}
}
But whats the point of having a default implementation of a property in an interface if you always have to cast it ugly?!
So according to the casting-solution of above I've tried this:
public interface IHasFirstNames
{
string? FirstName => FirstNames.FirstOrDefault();
List<string> FirstNames { get; }
}
public class Monster : IHasFirstNames
{
// Not ideal to declare the property here
// But at least the implementation is still in the interface
public string? FirstName => ((IHasFirstNames)this).FirstName;
public List<string> FirstNames { get; } = new();
public Monster(params string[] firstNames)
{
FirstNames.AddRange(firstNames);
}
}
public static class Program
{
public static void Main()
{
var sally = new Monster("James", "Patrick");
var error = sally.FirstName; // StackOverflow!
}
}
But against expectations this leads to a stack overflow as the cast to IHasFirstName
does not really call the default implementation of the interface.
Even when I implement a full getter with a dedicated variable of type IHasFirstName
it leads to a stack overflow.
The only ugly solution I've come up with is this with a dedicated getter method:
public interface IHasFirstNames
{
// Default implementation of a property is no use to me!
string? FirstName { get; }
// So I have to implement a getter method as default
public string? FirstNameGetter() => FirstNames.FirstOrDefault();
List<string> FirstNames { get; }
}
public class Monster : IHasFirstNames
{
public string? FirstName => ((IHasFirstNames)this).FirstNameGetter();
public List<string> FirstNames { get; } = new();
public Monster(params string[] firstNames)
{
FirstNames.AddRange(firstNames);
}
}
public static class Program
{
public static void Main()
{
var sally = new Monster("James", "Patrick");
var works= sally.FirstName;
}
}
It doesn't have to be a method. Apparently it's also OK if its a property with a different name. Once the property in the interface and in the class should have the same name it gets ugly.
Is there really no nicer solution for this?
thanks
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
正如其他人指出的那样,这并不是默认接口方法的预期用法。如文档所述:
对于您想要使用的方式,还有另一种可用的机制:静态扩展方法。正如您可能已经知道的那样,该机制在CLR中广泛用于诸如
iEnumerable&lt; t&gt;
扩展方法之类的东西。对于您的情况,您可以在接口旁边包含以下静态扩展类:
如果您使用该类别而不是接口本身中的声明,则代码将按预期工作。
但是,这当然会遭受实施类无法改变实现的主要缺点!如果您想支持这一点,则需要使用抽象基础类:
请注意,在此用法模式中,您的实现类始终源自
hasfirstnamesbase
来使用默认实现。派生的类可以覆盖实现:
然后:
As others have pointed out, this isn't really the intended usage for default interface methods. As the documentation states:
For the way you want to use it, there's another mechanism available: Static extension methods. As you're probably already aware, this mechanism is used extensively in the CLR for things such as
IEnumerable<T>
extension methods.For your case, you could include the following static extension class alongside the interface:
If you use that instead of the declaration in the interface itself, your code will work as expected.
However, this does of course suffer from the major drawback that implementing classes cannot change the implementation! If you want to support that, you need to use abstract base classes instead:
Note that in this usage pattern, your implementing classes always derive from
HasFirstNamesBase
to make use of the default implementation.Derived classes can override the implementation:
Then: