16

我正在查看Token Replacement and Identification的解决方案:

string result = Regex.Replace(
    text,
    @"\[%RC:(\d+)%\]",
    match => dict[int.Parse(match.Groups[1].Value)]);

而且我不明白如何MatchEvaluator超载。

我了解一些 lambda 表达式。它接受输入match,然后从字典中查找一个整数?

但是 for 的价值match从何而来?从哪里返回值match => dict[int.Parse(match.Groups[1].Value)]);

编辑:你们中有些人提到了Delegate. 令人惊讶的是,在 CS 大学三年后,我还没有遇到过这个学期。在这种特定情况下,它是什么Delegate以及它做了什么?

最后编辑:我尝试使用以下代码编写我自己的委托我的令牌的形式是[@someTokenName]

public void CreateScript(Dictionary<string,string> dictionary, string path)
    {
        //used in the regex to identify the string to replace
        var pattern = @"\[@[0-9a-fA-F]+\]";
        //read in the file found at the path
        var lines = File.ReadAllLines(path);
        int lineCounter = 0;
        foreach (string line in lines)
        {
            line = Regex.Replace(line, pattern, match => dictionary.TryGetValue((match.Groups[0].Value)));
        }

但是我不断收到“无法将 lambda 表达式转换为类型”的消息,因为它不是委托类型。我写的那一行和解决方案中找到的那一行有什么区别?

4

4 回答 4

58

这是一个与 lambda 匹配的正则表达式,因此比常规的旧 lambda 稍微复杂一些。开始了:

鉴于:

String result = Regex.Replace(
    text,
    @"\[%RC:(\d+)%\]",
    match => dict[int.Parse(match.Groups[1].Value)]);

(1) text - 我们正在搜索的文本。

(2) @"\[%RC:(\d+)%\]"- 意味着找到任何看起来像“[%RC:{number}%]”的东西,其中{number}显然是某个数字(因为\d意味着“一个数字”,并且\d+意味着“一个或多个连续的数字”)。还要注意{number}or\d+( )as in包围(\d+)。这很重要,因为这意味着数字是一个“组”,这与我们下面的解释有关。组是一种从正则表达式中提取“有用”部分的方法。也就是说,我们不想要整个匹配,只想要数字值

(3) 当它找到一个匹配时,它执行这个:match => dict[int.Parse(match.Groups[1].Value)]);

让我们从这部分开始:match => ...,它实际上与:

public String MatchEval(Match match)
{

}

请记住,lambda 表达式本质上只是常规函数的简写(除了编译器match根据它所代表的委托推断类型和返回类型 - 这里是MatchEvaluator - 稍后会详细介绍)。在这里,输入match作为输入传递给 lambda 表达式。然后你就有=>了类似于{ }我们在MatchEval上面的函数中看到的函数体的开头。结果,每次找到匹配项时,都会运行与此块等效的代码:

public String MatchEval(Match match)
{
    // Here we grab the value from group (1) (the number in parentasis from our Regex)
    return dict[int.Parse(match.Groups[1].Value)];
}

简而言之,请记住 lambda 只是函数的简写符号。如果您查看Regex.Replace的文档,您会看到 lambda 代表MatchEvaluator,其定义为:

public delegate string MatchEvaluator(Match match);

这与我们上面的功能扩展一致。事实上,你可以简单地写:

String result = Regex.Replace(
    text,
    @"\[%RC:(\d+)%\]",
    MatchEval);

(假设dict可以从单独的方法访问)并且该函数的工作原理与证明 lambda 只是完整函数的较短表示法相同。

编辑: 至于你的问题的第二部分,“什么是委托”,委托基本上解决了这个问题:“我不知道我想使用什么功能,但我知道它有什么签名”。考虑:

// This allows us to point to a math function with this signature,
// namely, takes two Int32 inputs, and returns an Int32.
public static delegate Int32 MathDelegate(Int32 lhs, Int32 rhs);

public static Int32 Add(Int32 lhs, Int32 rhs)
{
    return lhs + rhs;
}

// Note the variable names aren't important, just their TYPE
public static Int32 Subtract(Int32 a, Int32 b)
{
    return a - b;
}

static void Main()
{
    // We can use a delegate to point to a "real" function
    MathDelegate mathPerformer = Add;

    Console.WriteLine(mathPerformer(2, 3)); // Output : 5

    // Now let's point to "Subtract"
    mathPerformer = Subtract;

    Console.WriteLine(mathPerformer(2, 3)); // Output : -1

    Console.ReadLine();
}

当您在程序已经运行之前不知道要使用什么特定算法或处理技术时,这很有用。委托让我们选择要指向的函数,然后我们可以在程序运行时执行它。

这一切都与上面的 lambda 讨论有关,MatchEvaluator它不知道如何处理它在仔细阅读您的字符串时找到的每个匹配项。相反,通过为它提供一个 lambda/函数,你告诉它在找到匹配项时你想使用什么算法。基本上,委托对于在运行时确定您希望如何执行某些操作很有用。

编辑:如果您想扩展您的 lambda 表达式以包含多行代码,您也可以使用代码块。考虑:

String result = Regex.Replace(
    text,
    @"\[%RC:(\d+)%\]",
    match => { 
       return dict[int.Parse(match.Groups[1].Value)]
    });

你会注意到这里有两点不同。(1) 我们=>现在紧随其后,{ }它允许我们输入多行代码。结果,编译器不知道哪个值是返回值,因此无法推断返回类型是什么。因此,(2)我们插入一个显式return命令来指示哪个值是应该返回的值。

使用这个简单的代码库,我们可以执行以下操作:

String result = Regex.Replace(
    text,
    @"\[%RC:(\d+)%\]",
    match => { 
       // This does the same thing, but with more lines of code.
       // Of course, you could get way more fancy with it as well.
       String numericValueAsString = match.Groups[1].Value;
       Int32 numericValue = Int32.Parse(numericValueAsString);
       String dictionaryValue = dict[numericValue];

       // Same as above
       return dictionaryValue;
    });
于 2013-05-21T19:18:29.733 回答
7

想象:

string result = Regex.Replace(
text,
@"\[%RC:(\d+)%\]",
lambda);

//public delegate string MatchEvaluator(Match match)
string lambda(Match match) {
   return dict[int.Parse(match.Groups[1].Value)]); 
}

lambda 表达式的类型与 相同MatchEvaluator:for 的值match来自正则表达式(就像 lambda 表达式被定义为正则委托一样),lambda 表达式返回的值result再次分配给 ,就像如果它被定义为常规代表。

于 2013-05-21T19:11:22.543 回答
5

用简单的英语:

对于表达式匹配的所有组,使用 1 值索引处的组,将值解析为 anint并将其用作索引器/键,以从dictionary object被调用的值中提取值dict以用作替换新值。

lambda 只是使用匹配作为参数并在参数的上下文中执行主体的匿名函数。

于 2013-05-21T19:07:04.703 回答
3

lambda 只是定义一个匿名函数。该函数被传递给Replace方法(在这种情况下)。 Replace然后可以用这种方法做任何它想做的事。您需要查看该特定方法的文档,以了解它是如何使用提供的委托的;你需要依靠它来告诉你参数来自哪里,它对返回值做了什么,等等。

该方法的 MSDN 页面声明,作为该参数的描述:

检查每个匹配项并返回原始匹配字符串或替换字符串的自定义方法。

所以我们知道,对于每一个找到的匹配,它都会调用这个方法,传递Match代表它找到的匹配的对象作为参数。然后它将使用该方法的string返回值(我知道它是一个字符串,因为该委托的定义如此声明)作为替换找到的匹配项的内容。

于 2013-05-21T19:06:19.007 回答