我理解为什么宏不包含在语言中;他们很容易被滥用。然而,我现在又一次偶然发现它们似乎令人满意的情况。就在最近,我正在编写一种记录图像文件的方法:
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#中是否存在每处理器定义。我从一开始就明白我知道他们不这样做的断言。我的问题是参考我可能用来代替的技术。我相信这是在明确的答案中提供给我的。
答案 0 :(得分: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#支持它,这正是您对宏的问题({{1无效),除了它不会在运行时之前抛出异常。所以在安全方面也不是那么热。
就个人而言,我只是再次输入名称。我甚至不会使用nameof(getMyProcessedImage())
。以变量名称记录图像对我来说似乎是一种可疑的做法。如果我 希望明确引用生成位图的源中的位置,我会使用调用者信息:
nameof
简单地调用
public void LogImage(
Bitmap b,
[CallerFilePath] string filePath = "",
[CallerLineNumber] int lineNumber = 0
) {
LogImage(b, $"{Path.GetFileNameWithoutExtension(filePath)}({lineNumber})");
}
当然,这个确实假设你想要为每个位置提供唯一的位图,但是给出名称“logger”似乎是一个合适的假设。
答案 1 :(得分:0)
您可以利用匿名类型将自动生成属性名称的事实。首先定义一个像:
这样的重载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 });
这是对机制的滥用,在生产代码中,为了清楚起见,我可能会倾向于输入两次名称。在许多情况下,这也不是完全符合要求,但在这里我们讨论的是将图像保存到文件中,因此分配小对象和反射的额外成本可以忽略不计。