【问题标题】:What's the proper way to emulate C++ macros in C#在 C# 中模拟 C++ 宏的正确方法是什么
【发布时间】:2017-05-08 05:00:42
【问题描述】:

我明白为什么宏不包含在语言中;他们很容易被虐待。然而,我时不时地偶然发现它们看起来很可取的情况。就在最近,我正在编写一种记录图像文件的方法:

public void LogImage(Bitmap image, string name)
{
    image.Save(LogRootDirectory + name + ".bmp");
}

随后,我认为利用 nameof() 功能更好地提供有关我正在记录的图像的信息是很聪明的。在我的代码中的几个地方,我的行与此非常相似:

ImageLogger?.LogImage(processedImage, nameof(processedImage));

令我困扰的是它的重复性,我更喜欢类似这样的预处理器命令:

#define LOGIMAGE(img) ImageLogger?.LogImage(img, nameof(img));

有没有办法模拟上述宏行为以更好地简化我的代码?

注意:This 问题的不同之处在于作者询问 C# 中是否存在每个处理器的定义。我从一开始就明确表示我知道他们不知道的断言。我的问题是关于我可以替代使用的技术。我相信这是在标记的答案中提供给我的。

【问题讨论】:

  • C# 不支持宏,您最接近的是 Visual Studio 的 sn-ps
  • 在上述情况下,你能不能只提供一个重载方法 void LogImage(Bitmap image) 然后调用 LogImage(imager, nameof(image)) ?
  • VS Snippets 不像 c/c++ 宏
  • Knight 使用重载是行不通的。 Nameof(image) 将始终等于字符串“image”,而不是原始变量名。

标签: c#


【解决方案1】:

如果您对 C# 中的编译时技巧感到不知所措,那么您只是没有足够努力地滥用表达式树。

public void LogImage(Expression<Func<Bitmap>> b) {
    var memberExpression = b.Body as MemberExpression;
    if (memberExpression == null) throw new ArgumentException("Must be invoked with a member reference.");
    string name = memberExpression.Member.Name;
    Bitmap bitmap = b.Compile()();
    LogImage(bitmap, name);
}

调用为

ImageLogger?.LogImage(() => processedImage);

即使LogImage 从未被调用过,在调用站点生成表达式树也会产生一些开销,因此虽然这很聪明,但也不能过度使用。此外,这要求您的位图 具有 名称(getMyProcessedImage() 不起作用),如果 C# 支持宏,这正是您也会遇到的问题(nameof(getMyProcessedImage()) 无效),除了它不会在运行时引发异常。所以在安全方面也没有那么热。

就我个人而言,我只是再次输入名称。我什至不会使用nameof,真的。以变​​量名记录图像对我来说似乎是一种可疑的做法。如果我确实想要清楚地引用源中生成位图的位置,我会使用调用者信息:

public void LogImage(
    Bitmap b, 
    [CallerFilePath] string filePath = "", 
    [CallerLineNumber] int lineNumber = 0
) {
    LogImage(b, $"{Path.GetFileNameWithoutExtension(filePath)}({lineNumber})");
}

简单地调用

ImageLogger?.LogImage(processedImage);

当然,这确实假设您想要每个位置的唯一位图,但给定名称“logger”似乎是一个适当的假设。

【讨论】:

  • 是的,差不多就是这个。谢谢。
【解决方案2】:

您可以利用匿名类型将自动生成属性名称这一事实。首先定义一个像这样的重载:

public void LogImage(object obj)
{
    var prop = obj.GetType().GetProperties()[0];
    string name = prop.Name;
    Bitmap bitmap = (Bitmap)prop.GetValue(obj);
    LogImage(bitmap, name);
}

然后,当您想记录图像时,使用匿名类型调用它:

ImageLogger?.LogImage(new { processedImage });

这是对机制的滥用,在生产代码中,为了清楚起见,我可能倾向于输入两次名称。此外,在很多情况下,这并不完全有效,但这里我们讨论的是将图像保存到文件中,因此分配小对象和反射的额外成本可以忽略不计。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-11-20
    • 2011-11-29
    • 2011-08-29
    • 2012-05-09
    • 1970-01-01
    相关资源
    最近更新 更多