为什么内联方法不等于显式声明它?
你在编程中是否有盲点?
我的意思是,是否存在您无法真正习惯的通用技术或语言功能。 好吧,我有一个(或者可能不止一个),我的就是使用 delegate
。 举起手来!还有谁对代表感到不舒服?说实话!
那么什么是代表呢?
自从大学课程向我介绍了 C 语言以来,我就了解了函数指针。 如果您想将方法作为参数传递,函数指针会很方便。 所以在我看来,委托就像一个函数指针。尤里卡!我得到了它。我没有!
具体场景?
我想从文本文件中删除与正则表达式匹配的任何行。 假设我有一组行, List
的方法 RemoveAll
似乎非常适合该目的。 RemoveAll
需要一个评估方法作为参数来决定是否删除或保留列表元素。 这就是:函数指针!
这里有代码吗?
public static int RemoveLinesFromFile(string path, string pattern)
{
List<string> lines = new List<string>(File.ReadAllLines(path));
int result = lines.RemoveAll(DoesLineMatch);
File.WriteAllLines(path, lines.ToArray());
return result;
}
所以我正在寻找一个函数 DoesLineMatch 来评估一行是否与模式匹配。
你看到问题了吗?
RemoveAll
需要一个委托 Predicate
作为参数。 我会像这样编码:
private static bool DoesLineMatch(string line, string pattern)
{
return Regex.IsMatch(line, pattern);
}
但随后我收到错误“期望具有'boolDoesLineMatch(string)'签名的方法”。 我在这里缺少什么?
它到底有用吗?
这就是我最终让它工作的方式:
public static int RemoveLinesFromFile(string path, string pattern)
{
List<string> lines = new List<string>(File.ReadAllLines(path));
int result = lines.RemoveAll(delegate(string line)
{
return Regex.IsMatch(line, pattern);
});
File.WriteAllLines(path, lines.ToArray());
return result;
}
我很高兴它能工作,但我不明白它。
问题是什么?
为了让它工作,我所做的只是内联该方法。 据我了解内联,它只是某种使用一次并销毁的代码。 如果仅使用变量或方法一次,则可以内联它,但内联始终相当于显式声明它。
有没有办法显式声明该方法?我该怎么做?
PS.:请原谅我的问题有点长。
PPS.:一旦我得到了这个 delegate 的东西,我就会从 2.0 飞跃到 3.0 并学习 lambdas。
PPPS.:遵循乔恩的提示 关于 Regex.IsMatch(string, string)
的效率 我修改了我的代码:
int result = lines.RemoveAll(delegate(string line)
{
Regex regex = new Regex(pattern);
return regex.IsMatch(line);
});
这对于效率问题没有太大帮助。因此,我遵循 ReSharper 的提议,并将 Regex 实例化移至外部范围:
Regex regex = new Regex(pattern);
int result = lines.RemoveAll(delegate(string line)
{
return regex.IsMatch(line);
});
现在 ReSharper 敦促我将其替换为方法组:
Regex regex = new Regex(pattern);
int result = lines.RemoveAll(regex.IsMatch);
这与此处提出的答案非常相似。虽然不是我想要的,但我再次对 ReSharper(当然还有 Stack Overflow)如何帮助学习感到惊讶。
Do you have a blind spot in programming?
I mean is there a common technique or language feature that you can't really get used to.
Well, I have one (or probably more than one) and mine is usage of delegate
.
Hands up! Who else doesn't feel comfortable with delegates? Be honest!
So what's a delegate?
Since my courses at university introduced me to C, I know about function pointers.
Function pointers are handy if you want to pass methods as arguments.
So in my mind a delegate is something like a function pointer. Eureka! I got it. I have not!
A concrete scenario?
I would like to remove any line from a text file that matches a regular expression.
Assuming I have a collection of lines, List<T>
has method RemoveAll
which seems to be perfectly suitable for that purpose.RemoveAll
expects an evaluation method as argument for deciding on whether to remove or leave a list element.
And there it is: The function pointer!
Any code here?
public static int RemoveLinesFromFile(string path, string pattern)
{
List<string> lines = new List<string>(File.ReadAllLines(path));
int result = lines.RemoveAll(DoesLineMatch);
File.WriteAllLines(path, lines.ToArray());
return result;
}
So I'm looking for a function DoesLineMatch
which evaluates if a line matches a pattern.
Do you see the problem?
RemoveAll
expects a delegate Predicate<string> match
as argument.
I would have coded it like this:
private static bool DoesLineMatch(string line, string pattern)
{
return Regex.IsMatch(line, pattern);
}
But then I'm getting an error "Expected a method with 'bool DoesLineMatch(string)' signature".
What am I missing here?
Does it work at all?
This is how I finally got it working:
public static int RemoveLinesFromFile(string path, string pattern)
{
List<string> lines = new List<string>(File.ReadAllLines(path));
int result = lines.RemoveAll(delegate(string line)
{
return Regex.IsMatch(line, pattern);
});
File.WriteAllLines(path, lines.ToArray());
return result;
}
I'm happy that it works but I don't understand it.
And what is the question?
What I did to get it working is simply inlining the method.
As far as I understand inlining, it is just some kind of use-once-and-destroy-code.
If you use a variable or method only once you may inline it, but inlining is always equivalent to declaring it explicitly.
Is there a way to declare the method explicitly? How would I do it?
PS.: Pardon me that my question is somewhat lengthy.
PPS.: As soon as I get this delegate thing I will make the leap from 2.0 to 3.0 and learn lambdas.
PPPS.: Following Jon's hint on efficiency of Regex.IsMatch(string, string)
I modified my code:
int result = lines.RemoveAll(delegate(string line)
{
Regex regex = new Regex(pattern);
return regex.IsMatch(line);
});
That isn't of much help regarding efficiency matters. So I followed ReSharper's proposal and moved the Regex instantiation to the outer scope:
Regex regex = new Regex(pattern);
int result = lines.RemoveAll(delegate(string line)
{
return regex.IsMatch(line);
});
Now ReSharper urged me to replace this with a method group:
Regex regex = new Regex(pattern);
int result = lines.RemoveAll(regex.IsMatch);
And that is quite similar to the answers proposed here. Not what I asked for, but again I'm amazed how ReSharper (and Stack Overflow of course) helps learning.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(7)
您正在尝试使用具有以下签名的方法:
对于具有签名的委托:
它将从哪里获取第二个字符串值(模式)?
使用显式声明的方法执行此操作的唯一方法如下所示:
然后:
这与编译器使用匿名方法为您所做的操作很接近 - 它将创建一个嵌套类来保存捕获的变量 em> (在本例中是
pattern
)。(请注意,我避免讨论调用 Regex.Match(string, string) 的效率,而不是创建 Regex 的单个实例...这是一个不同的事情。)
You're trying to use a method with a signature of:
for a delegate with signature:
Where would it get the second string value (the pattern) from?
The only way to do this with an explicitly declared method would be something like this:
Then:
That's close to what the compiler's doing for you with the anonymous method - it will have created a nested class to hold the captured variable (
pattern
in this case).(Note that I've avoided any discussion of the efficiency of calling
Regex.Match(string, string)
rather than creating a single instance of theRegex
... that's a different matter.)基本上,您的匿名委托会导致编译器执行以下操作:生成一个名称难以发音的类,该类具有“模式”字段和类似于您在委托中编写的方法。
生成的类如下所示:
您看,此类将两个参数函数转换为一个参数函数。
您的代码将转换为类似的内容,
您看,运行时从作用域中获取变量并将其与函数绑定。现在您有了一个具有所需签名的函数,该函数包含附加变量。这就是为什么从 CS 的角度来看,委托被称为闭包。
当然,上面提到的一切都可以手动完成,这只是一种更简单的方法。
希望这有帮助。
Basically, your anonymous delegate causes compiler to do following: generate an class with unpronounceable name having a field 'pattern' and a method similar to written by you in a delegate.
Generated class looks like this:
You see, this class converts two argument function to a function with one argument.
Your code is converted to something like
You see, runtime takes a variable from scope and binds it with function. Now you have a function with required signature that encloses additional variable. That's why delegates are called closures from CS point of view.
Of course, everything mentioned can be made manually, this is just a more simple way of doing it.
Hope this helps.
为了扩展这里的一些其他答案,这里有一个 C# 的通用柯里化函数:
在您的示例中,您可以将参数的顺序切换到匹配函数,以便您要匹配的参数是第一个,如下所示:
然后您将使用柯里化来修复第一个参数,并获得一个委托,然后您可以将其转换为谓词,如下所示:
当然,您可以将其全部内联:
To expand on some of the other answers here, here's a generic currying function for C#:
In your example, you would switch the order of the arguments to your matching function, so that the parameter you want to match against is the first, like this:
Then you would use currying to fix the first parameter, and obtain a delegate that you could then convert to a predicate, like this:
of course, you can inline it all:
在 C# 2.0 中,您可以创建一个匿名委托,您可以使用它来捕获模式变量:
In C# 2.0 you can create an anonymous delegate, which you can use to capture your pattern variable:
这里你所遇到的现象是,C 程序员通常不会将具有不同参数的函数视为不同类型 - 他们不会想到将指针传递给具有两个字符串参数的函数,其中一个指针指向一个预计具有单个字符串参数的函数应该在编译时生成类型错误,就像在 Algol 68 中发生的那样。C
语言对此负有部分责任:事实上,它可以通过参数和返回类型正确键入函数指针。但这些类型的表示法确实很尴尬,C 编译器并不总是需要这个,当他们这样做时,程序员往往会通过将所有指针强制转换为 (void *) 来绕过它。
将 C 作为第一语言学习确实会教会你一些坏习惯。
What you've been bitten by here is the phenomenon that C programmers don't usually consider functions with different arguments as differently typed - it doesn't occur to them that passing a pointer to a function with two string arguments where a pointer to a function with a single string argument is expected should generate a type error at compile time, as would happen in e.g. Algol 68.
The C language is only partially to blame for this: it can in fact properly type function pointers by argument and return types. But the notation for these types is really awkward, C compilers don't always require this, and when they do programmers tend to get around it by casting all pointers to (void *) anyway.
Learning C as a first language does teach you some bad habits.
乔恩说。
此外,在 C# 3 中,假设您仍然希望将
pattern
传递给您的方法,您可以选择使用 lambda:Wot Jon Says.
Furthermore, in C# 3 you might choose to use a lambda, assuming you still want to pass
pattern
to your method:您可以这样声明它:
其中模式是您类中的私有变量。但这有点难看,这就是为什么您可以声明内联 deleage 并为在您的RemoveLinesFromFile 方法中本地声明的模式变量使用闭包。
You could declare it like this:
Where pattern is a private variable in you class. But that's a bit ugly, that why you can declare the deleage inline and use a closure for the pattern variable that is declared localy in your RemoveLinesFromFile method.