我知道这个功能在C#中不存在,但PHP最近添加了一个名为Traits的功能,我认为这个功能起初有点傻,直到我开始考虑它.
假设我有一个名为的基类Client
.Client
有一个叫做的属性Name
.
现在我正在开发一个可供许多不同客户使用的可重用应用程序.所有客户都同意客户应该有一个名字,因此它属于基类.
现在客户A出现并表示他还需要跟踪客户的权重.客户B不需要重量,但他想跟踪高度.客户C想要跟踪重量和高度.
有了特征,我们可以制作权重和高度特征:
class ClientA extends Client use TClientWeight
class ClientB extends Client use TClientHeight
class ClientC extends Client use TClientWeight, TClientHeight
Run Code Online (Sandbox Code Playgroud)
现在,我可以满足所有客户的需求,而无需在课堂上添加任何额外的毛病.如果我的客户稍后回来并说"哦,我真的很喜欢这个功能,我也可以拥有它吗?",我只是更新了类定义以包含额外的特性.
你会如何在C#中实现这一目标?
接口在这里不起作用,因为我想要对属性和任何相关方法的具体定义,我不想为每个版本的类重新实现它们.
("客户",我指的是雇用我作为开发人员的文字人员,而"客户"我指的是编程课程;我的每个客户都有他们想要记录信息的客户)
c# code-reuse design-patterns traits default-interface-member
有人可以解释同一类上的相同函数的行为有何不同吗?
using System;
public class HelloWorld
{
public static void Main(string[] args)
{
MyUpperScript mAsdfScript = new MyUpperScript();
IInterface mAsdfInterface = mAsdfScript;
mAsdfScript.Foo();
mAsdfInterface.Foo();
}
}
public class MyUpperScript : MyScript
{
public void Foo()
{
Console.WriteLine("Upper Script");
}
}
public class MyScript : IInterface
{
}
public interface IInterface
{
public void Foo()
{
Console.WriteLine("Interface");
}
}
Run Code Online (Sandbox Code Playgroud)
我期望它在 Foo() 调用上都写“Upper Script”
请注意,如果我也从接口派生第二个类,它会按照我期望的方式工作 - 都打印“Upper Script”:
// now deriving for the interface too:
public class MyUpperScript : MyScript, IInterface
{ …
Run Code Online (Sandbox Code Playgroud) c# inheritance overriding interface default-interface-member
考虑以下代码:
interface I {
string M1() => "I.M1";
string M2() => "I.M2";
}
abstract class A : I {}
class C : A {
public string M1() => "C.M1";
public virtual string M2() => "C.M2";
}
class Program {
static void Main() {
I obj = new C();
System.Console.WriteLine(obj.M1());
System.Console.WriteLine(obj.M2());
}
}
Run Code Online (Sandbox Code Playgroud)
它在 .NET Core 3.1.402 中产生以下意外输出:
I.M1
C.M2
Run Code Online (Sandbox Code Playgroud)
类A
没有 的成员的隐式或显式实现I
,所以我希望默认实现用于C
,因为C
继承了 的接口映射A
并且没有显式地重新实现I
。根据 ECMA-334 (18.6.6) 和 C# 6.0 语言规范: …
根据https://blogs.msdn.microsoft.com/dotnet/2018/11/12/building-c-8-0/,C#8中的一个新功能是接口的默认实现.这个新功能是否也隐含地允许多重继承?如果没有,如果我尝试以下操作将会发生什么:
public interface A { int Foo() => 1; }
public interface B { int Foo() => 2; }
public class C : A, B { }
Run Code Online (Sandbox Code Playgroud) 如果我有一个像这样的默认接口方法:
public interface IGreeter
{
void SayHello(string name) => System.Console.WriteLine($"Hello {name}!");
}
Run Code Online (Sandbox Code Playgroud)
我可以让我的具体实现调用该默认方法吗?
public class HappyGreeter : IGreeter
{
public void SayHello(string name)
{
// what can I put here to call the default method?
System.Console.WriteLine("I hope you're doing great!!");
}
}
Run Code Online (Sandbox Code Playgroud)
所以调用:
var greeter = new HappyGreeter() as IGreeter;
greeter.SayHello("Pippy");
Run Code Online (Sandbox Code Playgroud)
结果如下:
// Hello Pippy!
// I hope you're doing great!!
Run Code Online (Sandbox Code Playgroud)
事实上,从实现类中调用 C# 接口默认方法表明我可以调用我的类未实现的方法,但正如预期的那样,添加对((IGreeter)this).SayHello(name);
内部的调用HappyGreeter.SaysHello
会导致堆栈溢出。
我知道抽象类是一种无法实例化的特殊类.抽象类只是被分类(继承自).换句话说,它只允许其他类继承它但不能实例化.优点是它为所有子类强制执行某些层次结构.简单来说,它是一种强制所有子类进行相同层次结构或标准的契约.
我也知道An接口不是一个类.它是由Interface这个词定义的实体.接口没有实现; 它只有签名或换言之,只是没有正文的方法的定义.作为与Abstract类相似的一个,它是一个契约,用于为所有子类定义层次结构,或者它定义特定的方法集及其参数.它们之间的主要区别在于类可以实现多个接口,但只能从一个抽象类继承.由于C#不支持多重继承,因此接口用于实现多重继承.
当我们创建一个接口时,我们基本上创建了一组方法,没有必须被实现的类覆盖的任何实现.它的优点是它为类提供了一种方法,使其成为两个类的一部分:一个来自继承层次结构,另一个来自接口.
当我们创建一个抽象类时,我们正在创建一个可能有一个或多个已完成方法的基类,但至少有一个或多个方法未完成并被声明为abstract.如果抽象类的所有方法都未完成,那么它与接口相同.
但 但 但
我注意到我们将在C#8.0中使用默认接口方法
也许我问它因为我只有1 - 2年的编程经验,但现在抽象类和界面之间的主要区别是什么?
我知道我们不能在界面中建立状态,它们之间只有一个区别吗?
鉴于 auto 属性编译为 a get_method
、 aset_method
和私有变量,并且自 C# 8 引入默认接口方法以来
接口中的属性可以有默认实现吗?
特别是仅获取属性?
为什么默认接口方法的行为在 C# 8 中发生了变化?过去的以下代码(当默认接口方法未发布时):
interface IDefaultInterfaceMethod
{
// By default, this method will be virtual, and the virtual keyword can be here used!
virtual void DefaultMethod()
{
Console.WriteLine("I am a default method in the interface!");
}
}
interface IOverrideDefaultInterfaceMethod : IDefaultInterfaceMethod
{
void IDefaultInterfaceMethod.DefaultMethod()
{
Console.WriteLine("I am an overridden default method!");
}
}
class AnyClass : IDefaultInterfaceMethod, IOverrideDefaultInterfaceMethod
{
}
class Program
{
static void Main()
{
IDefaultInterfaceMethod anyClass = new AnyClass();
anyClass.DefaultMethod();
IOverrideDefaultInterfaceMethod anyClassOverridden = new AnyClass();
anyClassOverridden.DefaultMethod();
} …
Run Code Online (Sandbox Code Playgroud) 我真的很喜欢 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;
}
}
Run Code Online (Sandbox Code Playgroud)
但是,如果你总是不得不把它改得丑陋,那么在接口中拥有属性的默认实现有什么意义呢?
所以根据上面的铸造解决方案我尝试过:
public …
Run Code Online (Sandbox Code Playgroud) 考虑带有委托的逆变接口定义:
public interface IInterface<in TInput>
{
delegate int Foo(int x);
void Bar(TInput input);
void Baz(TInput input, Foo foo);
}
Run Code Online (Sandbox Code Playgroud)
Baz
失败的定义出现错误:
CS1961
无效方差:类型参数“TInput
”必须在“IInterface<TInput>.Baz(TInput, IInterface<TInput>.Foo)
”上协变有效。'TInput
' 是逆变的。
我的问题是为什么?乍一看,这应该是有效的,因为Foo
委托与TInput
. 我不知道是编译器过于保守还是我遗漏了什么。
请注意,通常您不会在接口内声明委托,特别是这不会在早于 C# 8 的版本上编译,因为接口中的委托需要默认接口实现。
如果允许这个定义,有没有办法打破类型系统,或者编译器是否保守?
c# generics covariance contravariance default-interface-member
c# ×10
c#-8.0 ×7
interface ×3
inheritance ×2
code-reuse ×1
covariance ×1
generics ×1
oop ×1
overriding ×1
traits ×1