字符串以()开头以任何方式避免两次检查

本文关键字:检查 两次 何方 开头 任何方 字符串 | 更新日期: 2023-09-27 18:27:01

我有这段代码。这在当时并不明显,但编写的代码总是会选择第一个选项,因为"fc"answers"fcip"都以"fc"开头。

string fcportdelimit = "fc";
string fcipportdelimit = "fcip";
if (BlockToProcess[0].StartsWith(fcportdelimit)) 
{
    try
    {
        this.ParseFCInterface(BlockToProcess);
    }
    catch (Exception E)
    {
        throw;
    } 
}
else if (BlockToProcess[0].StartsWith(fcipportdelimit)) 
{
    try
    {
        this.ParseFCIPInterface(BlockToProcess);
    }
    catch (Exception E)
    {
        throw;
    } 
}

我查看了字符串类,但没有看到StartsWith()或Contains()以模式作为输入。我测试的字符串要么是模式fcN/N,要么是fcipN,其中N是一个数字。所以,我想我会不得不做这样的事情吗?

if (BlockToProcess[0].StartsWith(fcportdelimit || fcipportdelimit) 
{ 
    if (BlockToProcess[0].StartsWith(fcipportdelimit)
    { 
       // do something here
    } 
    else
    { 
       //since fcipportdelimit didn't match it must be an fcport
       //so do something else
    }
}

字符串以()开头以任何方式避免两次检查

我发现正则表达式很简单。以下是Regex.IsMatch:的示例

if (Regex.IsMatch(str, "^(?:fc|fcip)") {
  ...
}

^表示"anchor to The start"(或"starts with"),|表示"非此即彼",(?:...)表示分组。

然而,既然每个匹配都调用两个不同的方法,为什么不让它保持原样呢?我删除了额外的代码,使其更易于查看。

正如康拉德所指出的,条件句的顺序很重要。

var command = BlockToProcess[0];
if (command.StartsWith("fcip")) {
    this.ParseFCIPInterface(BlockToProcess); // ParseFCIP
} else if (command.StartsWith("fc") {
    this.ParseFCInterface(BlockToProcess);   // ParseFC
}

快乐的编码。

假设StartsWith("fcip")暗示StartsWith("fc"),只需先测试后者,然后嵌套第二个测试。

此外,您的try块完全冗余,不起任何作用。

if (BlockToProcess[0].StartsWith(fcportdelimit) { 
    if (BlockToProcess[0].StartsWith(fcipportdelimit) { 
        // do something here
    }
    else {
        // do something here
    }
}

(当然,第二次检查仍然包含一个冗余部分,因为它再次检查fc,但重构此检查只会降低代码的可读性,不一定有利于性能。)

进行两次比较没有错,比如:

if (BlockToProcess[0].StartsWith(fcportdelimit) 
    || BlockToProcess[0].StartsWith(fcipportdelimit))
{ 
    if (BlockToProcess[0].StartsWith(fcipportdelimit)
    { 
     // do something here
    } 
    else
    {  //since fcipportdelimit didn't match it must be an fcport
       //so do something else
    }
}

只需将其更改为:

if (BlockToProcess[0].StartsWith(fcipportdelimit)) 
{
}
else if (BlockToProcess[0].StartsWith(fcportdelimit)) 
{
}

至少你会得到一些有用的东西。效率在这里似乎不是一个问题。

怎么样?

BlockToProcess[0].StartsWith(fcportdelimit) || BlockToProcess[0].StartsWith(fcipportdelimit)

我测试的字符串要么是模式fcN/N或fcipN

因此,从逻辑上讲,您只需要进行一次测试。如果它不是以"fcip"开头,那么它必须以"fc"开头

string fcipportdelimit = "fcip";
try
{
    if (BlockToProcess[0].StartsWith(fcipportdelimit)) 
    {
        this.ParseFCIPInterface(BlockToProcess);
    } 
    else 
    {
        this.ParseFCInterface(BlockToProcess);
    }
}
catch (Exception E)
{
    throw;
} 

这个答案可能有些过头了,但应该会产生一个更易于维护的&可测试的解决方案。它还需要一些相当先进的测试&重构以保持现有行为。

这里的总体思想是,您提供的代码意味着单个解析类可能对单个操作了解太多。考虑到这一点,我最初会创建一个具有两个实现的IParser接口。

public interface IParser
{
    void Parse(string input);
    string RequiredPrefix { get; }
    /* you'll want to add anything that's appropriate for your code here */
}
public class FcPortParser : IParser { ... }
public class FcipPortParser : IParser { ... }

然后创建一个Factory,它将返回适当的给定输入的解析器:

public class ParserFactory
{
    private List<IParser> _knownParsers;
    public ParserFactory()
    {
        // you should be able to initialize this dynamically but that's off-topic here
        _knownParsers = new List<IParser> { new FcPortParser(), new FcipPortParser() };
    }
    public IParser GetFromString(string given)
    {
        // return the IParser with the longest prefix that given starts with
        // this will return null for a non-match
        return _knownParsers.Where(p => given.StartsWith(p.RequiredPrefix))
                            .OrderByDescending(p => p.RequiredPrefix.Length)
                            .FirstOrDefault();  
    }
}

有了这个解决方案,您可以轻松地测试各个解析器以及返回适当解析器的工厂。

//NUnit
Assert.IsInstanceOf<FcipPortParser>(factory.GetFromString("fcip..."));
Assert.IsInstanceOf<FcPortParser>(factory.GetFromString("fc..."));
Assert.IsNull(factory.GetFromString("undefined..."));

你的代码现在变成这样:

var parser = _factory.GetFromString(BlockToProcess[0]);
if (parser != null)  
{
     parser.Parse(BlockToProcess[0]);
}

如果你想避免parser != null检查,你可以添加一个像这样的空对象实现(并确保工厂接受它):

public class NullParser : IParser
{
    public void Parse(string given) { /* do nothing */ }
    // should return true for every non-null string
    public string RequiredPrefix { get { return string.Empty; } }
}

现在,当您需要添加功能时,只需添加一个新的IParser类(如果工厂没有动态初始化,则添加工厂)。

public class FcNewParser : IParser
{
    public void Parse(string given) { ... }
    public string RequiredPrefix { get { return "fcnew"; } }
}

按相反的顺序执行,首先检查fcipportdelimite,然后检查else块中的fcportdelimite。这将解决您的问题

string fcportdelimit = "fc";
string fcipportdelimit = "fcip";
 if (BlockToProcess[0].StartsWith(fcipportdelimit)) 
                        {
                            try
                            {
                                this.ParseFCInterface(BlockToProcess);
                            }
                            catch (Exception E)
                            {
                                throw;
                            } 
                        }
                        else if (BlockToProcess[0].StartsWith(fcportdelimit)) 
                        {
                            try
                            {
                                this.ParseFCIPInterface(BlockToProcess);
                            }
                            catch (Exception E)
                            {
                                throw;
                            } 
                        }