在c#: PostSharp或T4模板中生成不可变值对象
本文关键字:不可变 对象 PostSharp T4 | 更新日期: 2023-09-27 17:49:30
我已经厌倦了模板式的不可变值对象代码。是否PostSharp或T4模板允许我做以下转换?
输入:public struct Name
{
public string FirstName;
public string LastName;
}
输出:public struct Name : IEquatable<Name>
{
private readonly string firstName;
private readonly string lastName;
public string FirstName { get { return this.firstName; } }
public string LastName { get { return this.lastName; } }
public Name(string firstName, string lastName)
{
this.firstName = firstName;
this.lastName = lastName;
}
public bool Equals(Name other)
{
return this.FirstName == other.FirstName && this.LastName == other.LastName;
}
public override bool Equals(object obj)
{
return obj is Name && this.Equals((Name)obj);
}
public override int GetHashCode()
{
unchecked
{
int hash = 17;
if (this.FirstName != null)
{
hash = hash * 29 + this.FirstName.GetHashCode();
}
if (this.LastName != null)
{
hash = hash * 29 + this.LastName.GetHashCode();
}
return hash;
}
}
public static bool operator==(Name a, Name b)
{
return a.Equals(b);
}
public static bool operator !=(Name a, Name b)
{
return !(a == b);
}
}
显然PostSharp需要[MakeImmutable]
注释,这很好。我希望根据原始类型生成class
或struct
的选项。还要注意,GetHashCode
必须省略null
对值类型成员的检查。
我很确定这两种技术都有这种能力,但我自己只是PostSharp方面/T4模板的消费者,我不知道哪一种技术更适合这项任务。不管你给出的答案是什么,我都会花一两天的时间来学习足够的东西来让它工作:)。
我做了一些你最近问的东西(使用T4模板),所以这是绝对可能的:
https://github.com/xaviergonz/T4Immutable例如:
[ImmutableClass(Options = ImmutableClassOptions.IncludeOperatorEquals)]
class Person {
private const int AgeDefaultValue = 18;
public string FirstName { get; }
public string LastName { get; }
public int Age { get; }
[ComputedProperty]
public string FullName {
get {
return FirstName + " " + LastName;
}
}
}
它会在一个单独的部分类文件中自动为您生成以下内容:
- 一个构造函数,如public Person(string firstName, stringlastName, int age = 18),将初始化值。
- Equals(object other)和Equals(Person other)的工作实现。它还将为您添加均衡界面。工作operator==和operator!的实现=
- GetHashCode()的工作实现一个更好的ToString()与输出,如"Person {FirstName=John, LastName=Doe, Age=21}"
- 一个人(…)的方法,可以用来生成一个新的不可变克隆与0或多个属性改变(例如var janeDoe = johnDoe)。与(firstName:"Jane",年龄:20)
所以它会生成这个(不包括一些多余的属性):
using System;
partial class Person : IEquatable<Person> {
public Person(string firstName, string lastName, int age = 18) {
this.FirstName = firstName;
this.LastName = lastName;
this.Age = age;
_ImmutableHashCode = new { this.FirstName, this.LastName, this.Age }.GetHashCode();
}
private bool ImmutableEquals(Person obj) {
if (ReferenceEquals(this, obj)) return true;
if (ReferenceEquals(obj, null)) return false;
return T4Immutable.Helpers.AreEqual(this.FirstName, obj.FirstName) && T4Immutable.Helpers.AreEqual(this.LastName, obj.LastName) && T4Immutable.Helpers.AreEqual(this.Age, obj.Age);
}
public override bool Equals(object obj) {
return ImmutableEquals(obj as Person);
}
public bool Equals(Person obj) {
return ImmutableEquals(obj);
}
public static bool operator ==(Person a, Person b) {
return T4Immutable.Helpers.AreEqual(a, b);
}
public static bool operator !=(Person a, Person b) {
return !T4Immutable.Helpers.AreEqual(a, b);
}
private readonly int _ImmutableHashCode;
private int ImmutableGetHashCode() {
return _ImmutableHashCode;
}
public override int GetHashCode() {
return ImmutableGetHashCode();
}
private string ImmutableToString() {
var sb = new System.Text.StringBuilder();
sb.Append(nameof(Person) + " { ");
var values = new string[] {
nameof(this.FirstName) + "=" + T4Immutable.Helpers.ToString(this.FirstName),
nameof(this.LastName) + "=" + T4Immutable.Helpers.ToString(this.LastName),
nameof(this.Age) + "=" + T4Immutable.Helpers.ToString(this.Age),
};
sb.Append(string.Join(", ", values) + " }");
return sb.ToString();
}
public override string ToString() {
return ImmutableToString();
}
private Person ImmutableWith(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return new Person(
!firstName.HasValue ? this.FirstName : firstName.Value,
!lastName.HasValue ? this.LastName : lastName.Value,
!age.HasValue ? this.Age : age.Value
);
}
public Person With(T4Immutable.WithParam<string> firstName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<string> lastName = default(T4Immutable.WithParam<string>), T4Immutable.WithParam<int> age = default(T4Immutable.WithParam<int>)) {
return ImmutableWith(firstName, lastName, age);
}
}
还有更多的功能,如项目页面中所解释的。
看起来你只是想装饰你的结构与样板的东西,所以PostSharp将是一种方式。原因是对于T4,您需要
- 查找所有需要更改的对象(仍然需要属性或某种类型的标记)
- 确保你还没有对结构体 进行修改
- 使用EnvDTE添加项目(不总是一致)
请参阅http://table2dto.codeplex.com查看T4的示例
要做到这一点,您需要使用EnvDTE(参见http://dfactor.codeplex.com获取如何做到这一点的代码)
问题是,如果你需要生成样板代码,你需要在设计时使用(它看起来不像你有什么),那么它将需要一些思考让PostSharp为你工作。
PostSharp将是你最好的选择
我建议使用ReSharper:对于这种情况,它可以很好地生成相等成员,所以你唯一需要写的就是一组属性getter。
还请注意,通常您必须在Equals
方法中区分结构体和对象,以便更快地检查相等性,因此如果这很重要,那么T4将不是一个好的选择。