c# - 如何在 C# 函数库中正确划分代码?

标签 c# functional-programming

作为关于可重用库(对于我正在学习的内容)的 FP 设计的一个关键区别的前提是,这些库比相应的 OO(通常)更以数据为中心。

TFD(类型优先开发)等新兴技术似乎也证实了这一点,Tomas Petricek 在 this 中对此进行了很好的解释。博文。

如今的语言是多范式的,同一位 Petricek 在其书中解释了 C# 中可用的各种函数式技术。

我感兴趣的是如何正确划分代码

所以我定义了库数据结构,使用等效的可区分联合(如 Petricek 书中所示),并且我计划根据我的需求的域逻辑将它们与不可变列表和/或元组一起使用 .

我在哪里放置作用于数据结构的操作(方法...函数)?

如果我想定义一个使用标准委托(delegate)中体现的函数值的高阶函数 Func<T1...TResult> ,我应该把它放在哪里?

常识告诉我应该将这些方法分组到静态类中,但我希望得到已经用 C# 编写过函数库的人的确认。

假设这是正确的并且我有一个像这样的高阶函数:

static class AnimalTopology {
  IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector) {
    // remainder omitted
  }
}

如果 choosing vertebrated animal 有 N 个我想在库中公开的特定案例,那么公开它们的更正确方法是什么。

static class VertebratedSelectorsA {
  // this is compatible with "Func<Skeleton, bool> selector"
  static bool Algorithm1(Skeleton s) {
    //...
  } 
}

static class VertebratedSelectorsB {
  // this method creates the function for later application
  static Func<Skeleton, bool> CreateAlgorithm1Selector(Skeleton s) {
     // ...
  } 
}

任何指示将不胜感激。

编辑:

我想引用 T. Petricek 的两个短语,Real World Functional Programming Mads Torgersen 的前言:

[...] You can use functional programming techniques in C# to great benefit, though it is easier and more natural to do so in F#. [...] Functional programming is a state of mind. [...]

EDIT-2:

  • 我觉得有必要进一步澄清这个问题。标题中提到的functionalFunctional Programming严格相关;我不是在问更功能性的方法分组方式,即更逻辑的方式或更一般意义上的方式。

  • 这意味着实现将尽可能多地遵循由 NOOO 总结的 FP 的创始概念。为了方便和清晰起见,在此引用声明:

  1. Functions and Types over classes
  2. Purity over mutability
  3. Composition over inheritance
  4. Higher-order functions over method dispatch
  5. Options over nulls

问题是关于如何根据 FP 概念编写 C# 库的布局,因此(例如)将方法放在数据结构中绝对不是一个选项;因为这是一个创始的面向对象范例。

EDIT-3:

此外,如果问题得到回应(和各种评论),我不想给人一种错误的印象,即有人说一种编程范式优于另一种。 和以前一样,我会在其 Expert F# 3.0(第 20 章 - 设计 F# 库 - 第 565 页)一书中提到 FP 的权威 Don Syme:

[...] It's a common misconception that the functional and OO programming methodologies compete; in fact, they're largely orthogonal. [...]

最佳答案

Note: If you want a shorter, more-to-the-point answer, see my other answer. I am aware that this one here might seem to ramble & go on forever & talk past your issue, but perhaps it will give you a few ideas.

如果不知道 Animal 之间的确切关系,很难回答您的问题和 Skeleton .我将在回答的后半部分就这种关系提出建议,但在此之前,我将简单地赞同我在您的帖子中看到的内容。

首先我会尝试从您的代码中推断出一些事情:

static class AnimalTopology
{
    // Note: I made this function `static`... or did you omit the keyword on purpose?
    static IEnumerable<Animal> ListVertebrated(Func<Skeleton, bool> selector)
    {
        …
    }
}
  1. 如果您根据功能原则设计了该功能,那么它应该没有副作用。也就是说,它的输出仅依赖于它的参数。 (在半面向对象的设置中,可能在 AnimalTopology 的其他静态成员上;但由于您没有显示任何内容,让我们忽略这种可能性。)

  2. 如果函数确实没有副作用(并且不访问 AnimalTopology 的静态成员),那么函数的类型签名表明可以派生 Animal。来自Skeleton ,因为它接受作用于 Skeleton 的东西s 和返回 Animal

  3. 如果这也是真的,那么为了能够给出答案,让我假设如下:

    class Skeleton
    {
        …
        public Animal Animal { get { … } } // Skeletons have animals!? We'll get to that.
    }
    

现在很明显你的函数无法实现,因为它可以派生Animal来自 Skeleton s,但它没有收到任何 Skeleton根本;它只接收作用于 Skeleton 的谓词函数. (您可以通过添加类型为 Func<IEnumerable<Skeleton>> getSkeletons 的第二个参数来解决此问题,但是...)

在我看来,像下面这样的东西会更有意义:

static IEnumerable<Animal> GetVertebrates(this IEnumerable<Skeleton> skeletons,
                                          Func<Skeleton, bool> isVertebrate)
{
    return skeletons
           .Where(isVertebrate)
           .Select(s => s.Animal);
}

现在,您可能想知道为什么您要根据骨骼来猜测动物;并且不是 bool属性“是脊椎动物”是动物(或骨骼)的固有属性吗?真的有几种方法可以决定这一点吗?

我建议如下:

class Animal
{
     Skeleton Skeleton { get; } // not only vertebrates have skeletons! 
}

class Vertebrate : Animal { … } // vertebrates are a kind of animal 

static class AnimalsExtensions
{
    static IEnumerable<Vertebrate> ThatAreVertebrates(this IEnumerable<Animal> animals)
    {
        return animals.OfType<Vertebrate>();
    }
}

请注意上面扩展方法的使用。这是一个如何使用它的示例:

List<Animal> animals = …;
IEnumerable<Vertebrate> vertebrates = animals.ThatAreVertebrates();

现在假设您的扩展方法做了更复杂的工作。在这种情况下,将其放入自己指定的“算法类型”中可能是个好主意:

interface IVertebrateSelectionAlgorithm
{
    IEnumerable<Vertebrate> GetVertebrates(IEnumerable<Animal> animals);
}

这具有可以设置/参数化的优点,例如通过类构造函数;并且您可以将算法拆分为几个方法,它们都位于同一类中(但都是 private 除了 GetVertebrates 。)

当然,您可以使用函数式闭包进行相同类型的参数化,但根据我的经验,这在 C# 设置中很快就会变得困惑。在这里,类是将一组功能组合在一起作为一个逻辑实体的好方法。

关于c# - 如何在 C# 函数库中正确划分代码?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15691047/

相关文章:

C# OpenXml Excel Creation - 我可以将它设置为 "Format as table"吗?

javascript - 具有一流功能的语言是否一定允许闭包?

c++ - 我怎样才能实现一个函数来调用任何(任意)函数及其(任意)参数?

functional-programming - Erlang:做一个环

javascript - 如果一个函数组件具有内部可变状态,它可以被认为是纯函数组件吗?

javascript - 哈希函数的函数式编程纯度要求

c# - 使用文件上传控件过滤文件类型

c# - 如何在 C# 中使用 Dictionary<> 的聚合方法?

c# - Netsuite:如何将采购订单链接到销售订单

c# - 如何将字符串参数从 C++ com 传递给 C#?