且构网

分享程序员开发的那些事...
且构网 - 分享程序员编程开发的那些事

您将如何实现“特质"?C#中的设计模式?

更新时间:2022-12-08 13:32:52

您可以通过使用标记接口和扩展方法来获取语法.

You can get the syntax by using marker interfaces and extension methods.

先决条件:接口需要定义扩展方法稍后使用的契约.基本上,接口定义了能够实现"特征的契约;理想情况下,您添加接口的类应该已经存在接口的所有成员,这样不需要额外的实现.

Prerequisite: the interfaces need to define the contract which is later used by the extension method. Basically the interface defines the contract for being able to "implement" a trait; ideally the class where you add the interface should already have all members of the interface present so that no additional implementation is required.

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

提出了如何存储额外数据的问题.这也可以通过做一些额外的编码来解决:

The question was raised how additional data could be stored. This can also be addressed by doing some extra coding:

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;
  }
}

然后,如果特征接口"继承自IDynamicObject,则特征方法可以添加和检索数据:

And then, the trait methods can add and retrieve data if the "trait interface" inherits from 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 关键字一起使用时,使对附加属性的访问变得透明.

Note: by implementing IDynamicMetaObjectProvider as well the object would even allow to expose the dynamic data through the DLR, making the access to the additional properties transparent when used with the dynamic keyword.