C#玩F#类型并迷路



我一直在f#上进行一些阅读,并决定尝试一下。我从一个有点参与的例子开始,我想到了,立即迷路了。我想知道是否有人可以分享一些想法。

我想编写一种称为ComparisonStrategy<'T>的方法,该方法返回一个IEqualityComparer<'T>实例。它需要ComparisonWhichAndHow<'T>实例的可变长度。类型ComparisonWhichAndHow<'T>可以是:

  1. 类型('T -> *)的一个函数,这是一种选择单个字段以比较/li>的方法
  2. 如果您不希望在'U上使用默认的EqualsGetHashCode,则('T -> 'U, IEqualityComparer<'U>)的2元组。

我试图在Visual Studio上进行一段时间,但我什至无法正确获得函数声明部分。如果我能超越它,我有点肯定,我将能够实现该方法的主体,但似乎我不能。

编辑:

这是我到目前为止尝试过的代码。

我正在尝试实现以下两个事情。

  1. 提出了一种为每个对象生成平等方法的通用方法。
  2. 有时某些业务运营可能需要比较两个对象的某些字段以及其孩子的某些领域。不是一个完整的比较。我正在尝试使这些代码更简洁,简单

这是我到目前为止所拥有的:

module Failed =
    open System.Collections.Generic
    open System
    type ComparsionOption<'T, 'U> =
        | Compare of ('T -> 'U)
        | CompareWith of ('T -> 'U) * IEqualityComparer<'U>
    // TO USE: [<ParamArray>] 
    // TODO: this method returns a dummy for now
    let CompareStrategy (opts : ComparsionOption<'T, _> array) =
        EqualityComparer<'T>.Default
    // How it's used
    type Person(name : string, id : Guid) = 
        member this.Name = name
        member this.ID = id
    let fullCompare : EqualityComparer<Person> =
        CompareStrategy [|Compare(fun (p : Person) -> p.Name);
                            CompareWith((fun (p : Person) -> p.ID), EqualityComparer<Guid>.Default)|] // error here

从另一个角度查看问题,看起来您希望能够构建以两种不同的方式执行比较的对象(您指定的)然后构成它们。

让我们从构建执行比较的对象的两种方法开始。您可以通过IEqualityComparer<'T>表示。第一个采用函数'T -> Something并对结果进行比较。您可以定义这样的函数:

/// Creates a comparer for 'T values based on a predicate that 
/// selects some value 'U from any 'T value (e.g. a field)
let standardComparer (f:'T -> 'U) = 
  { new IEqualityComparer<'T> with
      member x.Equals(a, b) = 
        (f a).Equals(b)  // Call 'f' on the value & test equality of results
      member x.GetHashCode(a) = 
        (f a).GetHashCode() } // Call 'f' and get hash code of the result

该函数是使用F#generics的'T -> 'U,因此您可以投射任何类型的字段(类型只需可比性)。第二个原始函数还需要'T -> 'U,但它也需要对'U值进行比较,而不是使用默认值:

/// Creates a comparer for 'T values based on a predicate & comparer
let equalityComparer (f:'T -> 'U) (comparer:IEqualityComparer<'U>) = 
  { new IEqualityComparer<'T> with
      member x.Equals(a, b) = 
        comparer.Equals(f a, f b) // Project values using 'f' and use 'comparer'
      member x.GetHashCode(a) =
        comparer.GetHashCode(f a) } // Similar - use 'f' and 'comparer'

现在,您要说的是,您想采用以上两个方法之一创建的一系列值来构建单个比较策略。我不确定您的意思。当指定的比较将它们报告为相等时,您是否希望两个对象相等?

假设是这种情况,您可以编写一个结合两个 IEqualityComparer<'T>值的函数,并在两个比较报告以这样的比较报告时将其报告为平等:

/// Creates a new IEqualityComparer that is based on two other comparers
/// Two objects are equal if they are equal using both comparers.
let combineComparers (comp1:IEqualityComparer<'T>) (comp2:IEqualityComparer<'T>) =
  { new IEqualityComparer<'T> with
      member x.Equals(a, b) =
        comp1.Equals(a, b) && comp2.Equals(a, b) // Combine results using &&
      member x.GetHashCode(a) =
        // Get hash code of a tuple composed by two hash codes
        hash (comp1.GetHashCode(a), comp2.GetHashCode(a)) }

这本质上是实现所需的所有功能。如果您有一些对象Person,则可以这样构建比较:

// Create a list of primitive comparers that compare 
// Name, Age and ID using special 'idComparer'
let comparers =
  [ standardComparer (fun (p:Person) -> p.Name);
    standardComparer (fun (p:Person) -> p.Age);
    equalityComparer (fun (p:Person) -> p.ID) idComparer ]
// Create a single comparer that combines all of them...
let comparePerson = comparers |> Seq.reduce combineComparers

您可以使用过载的方法等将其包装在更面向对象的接口中,但是我认为以上示例显示了解决方案中您需要的所有重要组件。

btw:在示例中,我正在使用f#对象表达式来实现所有功能。

相关内容

  • 没有找到相关文章

最新更新