我知道 C# 中不存在此功能,但 PHP 最近添加了一个名为 Traits 的功能一开始我觉得这有点傻,直到我开始考虑它。
假设我有一个名为 Client
的基类。 Client
有一个名为 Name
的属性。
现在我正在开发一个可重复使用的应用程序,它将被许多不同的客户使用。所有客户都同意客户应该有一个名字,因此它在基类中。
现在客户 A 过来说他还需要跟踪客户的体重。客户 B 不需要体重,但他想跟踪高度。客户 C 想要跟踪体重和高度。
有了 traits,我们可以让 Weight 和 Height 特征都成为 traits:
class ClientA extends Client use TClientWeight
class ClientB extends Client use TClientHeight
class ClientC extends Client use TClientWeight, TClientHeight
现在我可以满足所有客户的需求,而无需在类(class)中添加任何额外的内容。如果我的客户稍后回来说“哦,我真的很喜欢那个功能,我也可以拥有它吗?”,我只需更新类定义以包含额外的特征。
您将如何在 C# 中完成此操作?
接口(interface)在这里不起作用,因为我需要属性和任何关联方法的具体定义,并且我不想为类的每个版本重新实现它们。
(“客户”是指雇用我作为开发人员的字面上的人,而“客户”是指编程类(class);我的每个客户都有他们想要记录其信息的客户)
最佳答案
您可以使用标记接口(interface)和扩展方法来获取语法。
先决条件:接口(interface)需要定义扩展方法稍后使用的契约。基本上,接口(interface)定义了能够“实现”特征的契约;理想情况下,您添加接口(interface)的类应该已经包含接口(interface)的所有成员,这样不需要额外的实现。
public class Client {
public double Weight { get; }
public double Height { get; }
}
public interface TClientWeight {
double Weight { get; }
}
public interface TClientHeight {
double Height { get; }
}
public class ClientA: Client, TClientWeight { }
public class ClientB: Client, TClientHeight { }
public class ClientC: Client, TClientWeight, TClientHeight { }
public static class TClientWeightMethods {
public static bool IsHeavierThan(this TClientWeight client, double weight) {
return client.Weight > weight;
}
// add more methods as you see fit
}
public static class TClientHeightMethods {
public static bool IsTallerThan(this TClientHeight client, double height) {
return client.Height > height;
}
// add more methods as you see fit
}
像这样使用:
var ca = new ClientA();
ca.IsHeavierThan(10); // OK
ca.IsTallerThan(10); // compiler error
编辑:提出了如何存储额外数据的问题。这也可以通过做一些额外的编码来解决:
public interface IDynamicObject {
bool TryGetAttribute(string key, out object value);
void SetAttribute(string key, object value);
// void RemoveAttribute(string key)
}
public class DynamicObject: IDynamicObject {
private readonly Dictionary<string, object> data = new Dictionary<string, object>(StringComparer.Ordinal);
bool IDynamicObject.TryGetAttribute(string key, out object value) {
return data.TryGet(key, out value);
}
void IDynamicObject.SetAttribute(string key, object value) {
data[key] = value;
}
}
然后,如果“特征接口(interface)”继承自 IDynamicObject
,特征方法可以添加和检索数据:
public class Client: DynamicObject { /* implementation see above */ }
public interface TClientWeight, IDynamicObject {
double Weight { get; }
}
public class ClientA: Client, TClientWeight { }
public static class TClientWeightMethods {
public static bool HasWeightChanged(this TClientWeight client) {
object oldWeight;
bool result = client.TryGetAttribute("oldWeight", out oldWeight) && client.Weight.Equals(oldWeight);
client.SetAttribute("oldWeight", client.Weight);
return result;
}
// add more methods as you see fit
}
注意:通过实现 IDynamicMetaObjectProvider
此外,该对象甚至允许通过 DLR 公开动态数据,从而在与 dynamic
关键字一起使用时使对其他属性的访问变得透明。
关于c# - 你将如何在 C# 中实现 "trait"设计模式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10729230/