IF实现在C#中

Cod*_*ist 5 c# linq functional-programming if-statement

大多数开发人员IF以下列方式编写语句

if (condition)
{
    //Do something here
}
Run Code Online (Sandbox Code Playgroud)

当然这被认为是正常的,但通常可以创建嵌套代码,这不是那么优雅,有点难看.所以问题是:是否可以将传统IF语句转换为功能语句?

**这是一个关于生成更易读代码的可能方法的开放性问题,我不想接受任何答案.我相信有更好的人选择自己最好的解决方案并投票给他们选择的答案.

lou*_*ter 8

虽然我已经添加了关于'functional if'anco条件表达式的响应.似乎有更多的东西被要求,那就是促进决策树.我提到使用monads作为答复中的最佳方法.这是如何完成的.如果你之前没有使用过monad,这可能看起来像伏都教,但它实际上是一种更好的方式来做@Gert Arnold发布的内容.

我将根据客户的适用性采用Gert的决策树.不使用lambdas做出决定的流畅风格,我将使用LINQ.这是C#对monad的原生支持.使用代码的结果如下所示:

        var client = new Client { 
            CriminalRecord = false, 
            UsesCreditCard = true, 
            YearsInJob = 10, 
            Income = 70000 
        };

        var decision = from reliable in ClientDecisions.Reliable
                       from wealthy in ClientDecisions.Wealthy
                       select "They're reliable AND wealthy";

        var result = decision(client);
        if (result.HasValue) Console.WriteLine(result.Value);

        decision = from reliableOrWealthy in Decision.Either(
                       ClientDecisions.Reliable,
                       ClientDecisions.Wealthy
                       )
                   from stable in ClientDecisions.Stable
                   select "They're reliable OR wealthy, AND stable";

        result = decision(client);
        if (result.HasValue) Console.WriteLine(result.Value);

        decision = from reliable in ClientDecisions.Reliable
                   from wealthy in ClientDecisions.Wealthy
                   from stable in ClientDecisions.Stable
                   select "They're reliable AND wealthy, AND stable";

        result = decision(client);
        if (result.HasValue) Console.WriteLine(result.Value);
Run Code Online (Sandbox Code Playgroud)

这种方法的优点在于它完全可以组合.您可以使用小的"决策部分"并将它们组合起来做出更大的决策.一切都没有必要if.

在上面的代码中,您将看到ClientDecisions静态类的用法.这是一些可重复使用的决策:

public static class ClientDecisions
{
    public static Decision<Client, Client> Reliable =>
        from client in Decision.Ask<Client>()
        where !client.CriminalRecord && client.UsesCreditCard
        select client;

    public static Decision<Client, Client> Wealthy =>
        from client in Decision.Ask<Client>()
        where client.Income > 100000
        select client;

    public static Decision<Client, Client> Stable =>
        from client in Decision.Ask<Client>()
        where client.YearsInJob > 2
        select client;
}
Run Code Online (Sandbox Code Playgroud)

注意这不是使用IEnumerableIQueryable.在C#中它可以提供Select,SelectMany而且Where对于任何类型的方法,如果正确实施,您可以进行任何类型转换为一元型.

有趣的是,在这个例子中,我将把一个代表变成一个monad.这是因为我们需要一个值传递给计算.这是委托的定义:

public delegate DecisionResult<O> Decision<I,O>(I input);
Run Code Online (Sandbox Code Playgroud)

上面例子中的输入是Client,输出是string.但请注意,上面的代表返回a DecisionResult<O>,而不是O(将是string).这是因为我们通过计算传播了一个名为的属性HasValue.如果HasValuefalse在任何点,则计算结束.这允许我们在计算期间做出决定,从而停止执行其余的计算.这是DecisionResult<T>班级:

public struct DecisionResult<T>
{
    private readonly T value;
    public readonly bool HasValue;

    internal DecisionResult(bool hasValue, T value = default(T))
    {
        this.value = value;
        HasValue = hasValue;
    }

    public T Value =>
        HasValue
            ? value
            : throw new DecisionFailedException();
}
Run Code Online (Sandbox Code Playgroud)

接下来,我们将添加一些将生成DecisionResult<T>值的辅助方法.他们将在被使用Select,SelectManyWhere方法之后.

public static class DecisionResult
{
    public static DecisionResult<O> Nothing<O>() =>
        new DecisionResult<O>(false);

    public static DecisionResult<O> Return<O>(O value) =>
        new DecisionResult<O>(true,value);
}
Run Code Online (Sandbox Code Playgroud)

既然我们有核心类型,我们可以编写我们的Select,SelectMany并将Where扩展方法写入Decision<I,O>委托(是的,你可以为委托编写扩展方法!)

public static class Decision
{
    public static Decision<I, V> Select<I, U, V>(this Decision<I, U> self, Func<U, V> map) =>
        input =>
        {
            var res = self(input);
            return res.HasValue
                ? DecisionResult.Return(map(res.Value))
                : DecisionResult.Nothing<V>();
        };

    public static Decision<I, V> SelectMany<I, T, U, V>(
        this Decision<I, T> self,
        Func<T, Decision<I, U>> select,
        Func<T, U, V> project) =>
        input =>
        {
            var resT = self(input);
            if (resT.HasValue)
            {
                var resU = select(resT.Value)(input);
                return resU.HasValue
                    ? DecisionResult.Return(project(resT.Value, resU.Value))
                    : DecisionResult.Nothing<V>();
            }
            else
            {
                return DecisionResult.Nothing<V>();
            }
        };

    public static Decision<I, O> Where<I, O>(this Decision<I, O> self, Predicate<O> pred) =>
        input =>
        {
            var res = self(input);
            return res.HasValue
                ? pred(res.Value)
                    ? DecisionResult.Return(res.Value)
                    : DecisionResult.Nothing<O>()
                : DecisionResult.Nothing<O>();
        };
}
Run Code Online (Sandbox Code Playgroud)

这些是允许Decision<I,O>在LINQ表达式中使用的方法.我们在Decision课堂上还需要一些额外的方法.首先Ask<I>,它只是获取传递给计算的值并允许它在表达式中使用.它的命名Ask是因为这个monad与ReaderHaskell 的标准monad 非常相似,而且它是ask按惯例调用的.

public static class Decision
{
    public static Decision<I,I> Ask<I>() =>
        input => DecisionResult.Return(input);
}
Run Code Online (Sandbox Code Playgroud)

我们也希望允许有条件的操作,所以我们也将添加Either<I,O>(...)Decision.这需要任意数量的Decision<I,O>monad,一个接一个地运行它们,如果一个成功,它会立即返回结果,如果没有继续.如果没有一个计算成功,那么整个计算将结束.这允许'或'类型行为,以及'切换'样式行为:

public static class Decision
{
    public static Decision<I, O> Either<I, O>( params Decision<I, O>[] decisions ) =>
        input =>
        {
            foreach(var decision in decisions)
            {
                var res = decision(input);
                if( res.HasValue )
                {
                    return res;
                }
            }
            return DecisionResult.Nothing<O>();
        };
}
Run Code Online (Sandbox Code Playgroud)

我们不需要'logical and',因为它可以实现为一系列from表达式或where表达式.

最后,我们需要一个异常类型,以防程序员尝试.ValueDecisionResult<T>何时使用.HasValue == false.

public class DecisionFailedException : Exception
{
    public DecisionFailedException()
        :
        base("The decision wasn't made, and therefore doesn't have a value.")
    {
    }
}
Run Code Online (Sandbox Code Playgroud)

使用这种技术(并在扩展方法的基础上),您可以避免if完全使用.这是一种真正的功能性技术.当然这不是惯用语,但它是你在C#中找到的最具声明性的方式.

  • 谢谢:)我做了一些小小的改进。将 DecisionResult&lt;T&gt; 更改为结构体,以便 CLR 更有可能将其保留在堆栈上,从而减少 GC 压力。 (2认同)

lou*_*ter 5

已经有一个'功能if'又名'条件表达式':

bool value = true;

var result = value 
             ? "Yes"
             : "No";
Run Code Online (Sandbox Code Playgroud)

它也可以在LINQ中使用

var q = from a in things
        select a > 10
               ? "Greater than 10"
               : "Less than or equal to 10";
Run Code Online (Sandbox Code Playgroud)

要么:

var q = from a in things
        let r = a > 10 
                ? "Greater than 10"
                : "Less than or equal to 10"
        select r;
Run Code Online (Sandbox Code Playgroud)

http://msdn.microsoft.com/en-us/library/aa691313(v=vs.71).aspx


Cod*_*ist 0

我想找到一种编写函数语句的方法,它看起来比简单If的更优雅If

简单的如果-否则:

void Main()
{
    (true && true).Then(() => Console.WriteLine("Printed out when condition == true"))
                  .Else(() => Console.WriteLine("Printed out when condition == false"));

    (true && false).Then(() => Console.WriteLine("Printed out when condition == true"))
                  .Else(() => Console.WriteLine("Printed out when condition == false"));

    Console.ReadKey();
}

public static class FunctionalExtensions
{
    private static bool OnConditionExecute(Action doSomething)
    {
        doSomething();
        return true;
    }

    //This is executed when condition == true
    public static bool Then(this bool condition, Action doSomething)
    {
        return (condition) && OnConditionExecute(doSomething);
    }

    //This is executed when condition == false
    public static bool Else(this bool condition, Action doSomething)
    {
        return (!condition) && OnConditionExecute(doSomething);
    }
}
Run Code Online (Sandbox Code Playgroud)

多重验证:

下面的代码解决了我首先提到的问题。它可以通过多个嵌套的 nasty 来实现If,但这种方法要好得多,并且证明了传统If语句并不适合所有地方!

void Main()
{
    Console.WriteLine(IsValid("hello"));
    Console.ReadKey();
}

public bool IsValid(string name)
{
    Func<string, bool>[] rules = 
    {
        // Some whatever validation rules...
        n => string.IsNullOrEmpty(name),
        n => n.Length < 5 || n.Length > 50,
        n => n.Equals("hello")
    };
    return rules.All(r => r(name) == false);
}
Run Code Online (Sandbox Code Playgroud)