我知道 F# 编译器可以生成 IEquatable<T>
的默认实现。和 IComparable<T>
,以及覆盖默认值 GetHashCode
和 Equals
记录和类的方法。但相比之下,这些实现使用了所有字段。我需要创建一些 DTO 类型,其中相等性和排序顺序应仅基于主键字段。有没有一些方便的机制,比如这个属性?
这是我的课:
(我可能犯了一些 F# 语法错误。还是有点新的。)
type Customer() =
member val Id = 0 with get, set
member val Name = "" with get, set
member val PhoneNumber = "" with get, set
我可以做这样的事情:
[<CompareByKey>]
type Customer() =
[<PrimaryKey>] member val Id = 0 with get, set
member val Name = "" with get, set
member val PhoneNumber = "" with get, set
而不是这个:
type Customer() =
member val Id = 0 with get, set
member val Name = "" with get, set
member val PhoneNumber = "" with get, set
override Equals(obj) =
match obj with
| :? Customer as c -> this.Id = c.Id
| _ -> false
override GetHashCode() = hash this.Id
interface IEquatable<Customer> with
member Equals(c) = this.Id = c.Id
interface IComparable<Customer> with
member CompareTo(c) = this.Id.CompareTo(c.Id)
编辑:
我认为这可能是使用抽象基类在 C# 中更好地解决的问题。 (也可以在 F# 中实现,但它有点开始变得不习惯了。)
public abstract class EntityBase<TEntity> :
IEquatable<TEntity>,
IComparable<TEntity>
where TEntity : EntityBase<TEntity> {
protected EntityBase(int id) {
this.Id = id;
}
public int Id { get; }
public sealed override int GetHashCode() => Id.GetHashCode();
public sealed override bool Equals(object obj) =>
Equals(this, obj as TEntity);
public bool Equals(TEntity other) =>
(other != null) && (this.Id == other.Id);
public int CompareTo(TEntity other) =>
(other == null) ? 1 : this.Id.CompareTo(other.Id);
}
最佳答案
您可以使用反射来拉出主键。反射可能很昂贵,因此请确保您只收到一次调用来搜索每种类型的键(我在下面的代码中留下了一些 printfn
以便我可以仔细检查)
open System
[<AttributeUsage(AttributeTargets.Property)>]
type PrimaryKeyAttribute() =
inherit Attribute()
[<CLIMutable()>]
type Customer =
{
[<PrimaryKey>]
Id: int
Name: string
}
let getPrimaryKey<'T, 'U when 'U:> IComparable> (): 'T -> 'U =
printfn "Starting to search for keys."
typeof<'T>.GetProperties()
|> Seq.tryFind (fun p ->
let attr = p.GetCustomAttributes(typeof<PrimaryKeyAttribute>, false)
attr.Length > 0
)
|> function
| Some p ->
(fun t -> p.GetMethod.Invoke(t, [| |]) :?> 'U)
| _ -> failwith "No PrimaryKey attribute found"
对客户列表进行排序现在变为:
let customers =
[
{ Id = 4; Name = "Alice" }
{ Id = 1; Name = "Eve" }
{ Id = 1; Name = "Charlie" }
{ Id = 1; Name = "Bob" }
]
let sorted =
customers
|> List.sortBy (getPrimaryKey())
你会得到:
val sorted : Customer list =
[{Id = 1;
Name = "Eve";}; {Id = 1;
Name = "Charlie";}; {Id = 1;
Name = "Bob";}; {Id = 4;
Name = "Alice";}]
而正常排序会给你鲍勃;查理;前夕;爱丽丝。您可能需要预先计算每种类型的 key getter:
let getCustomerKey<'U when 'U:> IComparable> : Customer -> 'U =
getPrimaryKey()
let sorted2 =
customers
|> List.sortBy getCustomerKey
而且,正如其他人已经指出的,
[<CLIMutable()>]
在处理依赖于默认构造函数和变异的代码时很有帮助。见 documentation here
关于.net - 在 F# 中,是否有使用用于相等和排序比较的主键创建记录类型的快捷方式?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38257283/