有没有办法使用扩展方法来实现下面的C#实现?

时间:2020-01-06 17:02:44

标签: c# linq

我有一种方法,其操作如下所示:

// check if bits 6,7,8 are zero
if ((num >> 5) != 0)
{
    //do some thing
    return false;
}

// check if bits 2 ,3,4 are zero
if ((num & 0x0E) != 0)
{
     //do something
     return false;
}

// check if bit 1 is 1
if ((num & 1) != 1)
{
    //dosomething
    return false;
 }

现在我想添加扩展方法,如:

   num
   .arebitsset((6,7,8) ,(do some action and return from method if false , if true allow chaining))
   .arebitsset(2,3,4) , <same as above>)
   .......

虽然我知道位集检查的逻辑,但是我需要知道如何从方法中返回或允许基于正确/错误结果进行链接。

使用func是否可能?我不确定。

注意:我有80个这样的条件要在下面进行测试,如果条件一定,最好写80个条件,当然不是,所以我需要一些紧凑的表格

4 个答案:

答案 0 :(得分:7)

您可以编写如下扩展方法:

public static class BitSetExtensions
{
    private static bool AreBitsSet(int i, int[] bits)
    {
        // you already have this
    }

    public static (int value, bool valid) CallActionIfBitsSet(this int value, int[] bits, Action action)
    {
        return CallActionIfBitsSet((value, true), bits, action);
    }

    public static (int value, bool valid) CallActionIfBitsSet(this (int value, bool valid) data, int[] bits, Action action)
    {
        if (data.valid)
        {
            data.valid = AreBitsSet(data.value, bits);

            if (data.valid) action();
        }

        return data;
    }
}

然后您可以像这样链接它们:

int num = 5;

num.CallActionIfBitsSet(new[] {1, 3, 5}, () =>
    {
        /* action */
    })
    .CallActionIfBitsSet(new[] {2, 3, 4}, () =>
    {
        /* other action */
    })
    .CallActionIfBitsSet(new[] {2, 3, 6}, () =>
    {
        /* other action */
    });

我个人不是一个大粉丝,因为我认为与传统的if相比,您的界面不会更容易使用,但是它可以正常工作。

答案 1 :(得分:0)

还有另一种方法可以使您满意。

您可以按照下面的代码定义结构。使用模式匹配并基于monad,特别是基于flatMap具有签名的函数(您可以在下面看到),可以创建一系列函数,这些函数将停止执行对调用者透明的完全调用,仅基于这些调用中发生的事情功能。

如果您搜索Rust的Option或Haskell的Maybe,则可以了解更多信息。这些语言使用的那些类型(求和类型)不是C#固有的,它们需要付出一些努力才能实现。

例如仅在下面的代码中

1 2 2我什么都没有!

当对FlatMap的第三次调用中断返回None的链时,将显示在控制台上。

这可以用于您的特定问题,但是也可以用于许多其他问题。

通过检查最终值的类型,您可以了解调用链是否成功。

此方法的优点是所有ifs对呼叫者都是完全透明的。

class Program
{
    static void Main()
    {
        var a = new Option<int>.Some(1);
        var result = a.FlatMap<int>(i => { Console.WriteLine(i); return new Option<int>.Some(i + 1); })
         .FlatMap<int>(i => { Console.WriteLine(i); return new Option<int>.Some(i); })
         .FlatMap<int>(i => { Console.WriteLine(i); return new Option<int>.None(); })
         .FlatMap<int>(i => { Console.WriteLine(i); return new Option<int>.Some(i); });

        switch (result)
        {
            case Option<int>.Some some:
                Console.WriteLine("I got Some!");
                break;
            case Option<int>.None none:
                Console.WriteLine("I got None!");
                break;
            default:
                throw new InvalidOperationException();
        }

    }
}

public abstract class Option<T>
{
    public class Some : Option<T>
    {
        public Some(T data)
        {
            Data = data;
        }

        public T Data;
    }

    public class None : Option<T> { }

    public Option<T> FlatMap<T>(Func<T, Option<T>> action)
    {
        switch (this)
        {
            case Option<T>.Some some:
                return action(some.Data);
            case Option<T>.None none:
                return none;
            default:
                throw new InvalidOperationException();
        }
    }
}

答案 2 :(得分:0)

您不能从方法链的中间返回。 您可以发明一种模仿这种行为的方法,但它并不是那么好,因为仅阅读方法调用的人就不会怀疑这种行为。

由于您也曾在评论中要求这样做,因此,如果您要避免使用不使用扩展方法的80个“如果”,那么另一种方法是列出测试和操作对的列表,如果测试成功,在foreach循环中测试每个。 我认为您不会获得任何可读性,但是您的代码中只会有一个“ if”。

使用像这样的简单类(或者,可以使用ValueTupple摆脱该类):

public class Test
{
    public Func<int, bool> Condition { get; set; }
    public Func<int, bool> Operation { get; set; }
}

您可以这样做:

    var Tests = new List<Test>
    {
        new Test
        {
            Condition = num => (num >> 5) != 0,
            Operation = num => { // Do stuff}
        },
        new Test
        {
            Condition = num => (num & 0x0E) != 0,
            Operation = num => { // Do other stuff}
        }
        // Add all your 80+ tests here
    };

// Then use a single if:
foreach (var test in Tests)
{
    if(test.Condition(Number))
    {
        test.Operation(Number);
        // Whatever the test that succeeded was, do here what you need to perform before returning, if needed.
        return false;
    }
}

答案 3 :(得分:0)

使用if语句检查条件是最有效和最容易调试的。如果发生异常,您将知道在哪里寻找错误。我唯一的建议是通过使用单个return false;和多个if来摆脱重复的else if语句:

public static bool Execute(int num)
{
    if ((num >> 5) != 0) // check if bits 6, 7, 8 are zero
    {
        // do something
    }
    else if ((num & 0x0E) != 0) // check if bits 2, 3, 4 are zero
    {
        // do something
    }
    else if ((num & 1) != 1) // check if bit 1 is 1
    {
        // do something
    }
    else
    {
        return false; // nothing has been done
    }
    return true; // something has been done
}