这在C#中可能吗?
我有一个测试扩展方法,因此我可以这样做:这在C#中可能吗?,c#,reflection,C#,Reflection,我有一个测试扩展方法,因此我可以这样做: var steve = new Zombie(); steve.Mood.ShouldBe("I'm hungry for brains!"); 扩展方法: public static void ShouldBe<T>(this T actual, T expected) { Assert.That(actual, Is.EqualTo(expected)); } 在扩展方法中,有没有什么可以用来获取属性“BrainsConsum
var steve = new Zombie();
steve.Mood.ShouldBe("I'm hungry for brains!");
扩展方法:
public static void ShouldBe<T>(this T actual, T expected)
{
Assert.That(actual, Is.EqualTo(expected));
}
在扩展方法中,有没有什么可以用来获取属性“BrainsConsumed”的名称的方法?奖励点是实例变量和类型Zombie
更新:
新的目标应该是:
public static void ShouldBe<T>(this T actual, T expected)
{
var frame = new StackTrace(true).GetFrame(1);
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber() - 1;
var code = File.ReadAllLines(fileName)
.ElementAt(lineNumber)
.Trim().TrimEnd(';');
var codeMessage = new Regex(@"(^.*)(\.\s*ShouldBe\s*\()([^\)]+)\)").Replace(code, @"$1 should be $3");
var actualMessage = actual.ToString();
if (actual is string)
actualMessage = "\"" + actual + "\"";
var message = string.Format(@"{0} but was {1}", codeMessage, actualMessage);
Assert.That(actual, Is.EqualTo(expected), message);
}
谢谢大家,尤其是马特·多森,这太棒了。顺便说一句,不要喂那些柔滑的巨魔。不幸的是,在这个位置上,你将无法在那个阶段获得财产的名称。问题是,您正在传入BrainsConsumed字段的值,而此时根本没有指向僵尸的链接(就您的方法而言,它是一个int,无法计算int最初来自何处)
我能为您提供的最好的东西是Environment.StackTrace中会包含相关信息,因为您已经调用了steve.BrainsConsumed 1 step up The stack(仅当您试图做的是了解单元测试中失败的部分时,才建议使用此选项,而不是在常规程序流中实际遍历堆栈时)不,我想你不行 让我们假设BrainsConsumed是一个整数(看起来很可能)。在这种情况下,参数是通过值传递的——您得到的只是正在测试的整数的副本。除了本地范围(实际)中的名称外,它没有其他名称 这个类似的问题可以澄清:
这将允许您测试它,但无法获得方法的名称 您可以使用此扩展:
public static void ShouldBe<T>(this Func<T> func, T expected)
{
Assert.AreEqual(func(), expected);
}
public static void应该是(应该是这个函数)
{
Assert.AreEqual(func(),应为);
}
使用以下ussage:
((Func<int>)Program.TestMethod).ShouldBe(2);
((Func)Program.TestMethod).应该是(2);
我能做的最好的事情是:
steve.Property(p => p.BrainsConsumed).ShouldBe(0);
或:
或:
关于:
奖励点数将是实例变量
通过使用Expression
(或仅使用Expression
),您可以相当轻松地获得属性名称和值。我将为中间部分做一个示例-注意,第一个示例需要DSL的额外类型,但没有什么繁重的内容:
public static class Test
{
public static void AssertEqual<TSource, TValue>(
this TSource source,
Expression<Func<TSource, TValue>> selector,
TValue expected)
where TSource : class
{
TValue value = selector.Compile()(source);
string paramName = selector.Parameters[0].Name;
System.Diagnostics.Debug.Assert(
EqualityComparer<TValue>.Default.Equals(value, expected),
typeof(TSource) + " " + paramName + ": " +
value + " doesn't match expected " + expected);
}
}
公共静态类测试
{
公共静态无效资产相等(
这是来源,,
表达式选择器,
t预期值)
where TSource:class
{
TValue=selector.Compile()(源代码);
字符串paramName=选择器。参数[0]。名称;
System.Diagnostics.Debug.Assert(
EqualityComparer.Default.Equals(值,预期值),
typeof(TSource)+“+paramName+”:“+
值+“与预期值不匹配”+预期值);
}
}
或者稍微好一点的信息:
public class Zombie
{
public int BrainsConsumed { get; set; }
static void Main() {
Zombie steve = new Zombie { BrainsConsumed = 2 };
Test.ShouldBeEqual(() => steve.BrainsConsumed, 0);
}
}
public static class Test
{
static string GetName(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess)
{
var me = (MemberExpression)expr;
string name = me.Member.Name, subExpr = GetName(me.Expression);
return string.IsNullOrEmpty(subExpr)
? name : (subExpr + "." + name);
}
return "";
}
public static void ShouldBeEqual<TValue>(
Expression<Func<TValue>> selector,
TValue expected)
{
TValue value = selector.Compile()();
string name = GetName(selector.Body);
System.Diagnostics.Debug.Assert(
EqualityComparer<TValue>.Default.Equals(value, expected),
typeof(TValue) + " " + name + ": " +
value + " doesn't match expected " + expected);
}
}
公共类僵尸
{
public int BrainsConsumed{get;set;}
静态void Main(){
僵尸史蒂夫=新僵尸{BrainsConsumed=2};
测试。应该是相等的(()=>steve.BrainsConsumed,0);
}
}
公共静态类测试
{
静态字符串GetName(表达式expr)
{
if(expr.NodeType==ExpressionType.MemberAccess)
{
var me=(MemberExpression)expr;
string name=me.Member.name,subExpr=GetName(me.Expression);
返回字符串.IsNullOrEmpty(subExpr)
?名称:(子表达式+“+”名称);
}
返回“”;
}
公共静态空间应该相等(
表达式选择器,
t预期值)
{
TValue=selector.Compile();
string name=GetName(selector.Body);
System.Diagnostics.Debug.Assert(
EqualityComparer.Default.Equals(值,预期值),
typeof(TValue)+“名称+”:“+
值+“与预期值不匹配”+预期值);
}
}
如果代码是调试生成的,可以使用一些诊断类来获取代码。考虑到这是针对单元测试的,调试可能是合理的
public static void ShouldBe<T>(this T actual, T expected)
}
例如,code=“steve.BrainsConsumed.ShouldBe(0);”
显然,您应该在此代码中添加一些错误检查,如果不读取文件中的所有行,您可能会加快检查速度。我不太明白为什么您会发布如此混乱且难以理解的代码,如果可以的话,这通常是一种不好的做法。但每个人都有自己的;这似乎有点不负责任。作为一个学习过程,这很好,但如果有人真的这样做了,他们应该被枪毙…@silky:哪一部分很难理解,你会如何让它更简单?呃。。。因为这是从代码表达式中获取属性名和/或变量名的最可靠的方法?Yuriy:我不打算解释对有能力自己解决它的人来说显而易见的事情。hmemcpy:我不会尝试解决它(因此我没有)。这不是一个合适的问题;我几乎可以肯定(尽管我可能是错的)这是没有合法用途的,而且基本目标可以以更好的方式实现。如果其他人对使用它感兴趣(你为什么不感兴趣!)
Assert.AreEqual(() => steve.BrainsConsumed, 0);
public static class Test
{
public static void AssertEqual<TSource, TValue>(
this TSource source,
Expression<Func<TSource, TValue>> selector,
TValue expected)
where TSource : class
{
TValue value = selector.Compile()(source);
string paramName = selector.Parameters[0].Name;
System.Diagnostics.Debug.Assert(
EqualityComparer<TValue>.Default.Equals(value, expected),
typeof(TSource) + " " + paramName + ": " +
value + " doesn't match expected " + expected);
}
}
public class Zombie
{
public int BrainsConsumed { get; set; }
static void Main() {
Zombie steve = new Zombie { BrainsConsumed = 2 };
Test.ShouldBeEqual(() => steve.BrainsConsumed, 0);
}
}
public static class Test
{
static string GetName(Expression expr)
{
if (expr.NodeType == ExpressionType.MemberAccess)
{
var me = (MemberExpression)expr;
string name = me.Member.Name, subExpr = GetName(me.Expression);
return string.IsNullOrEmpty(subExpr)
? name : (subExpr + "." + name);
}
return "";
}
public static void ShouldBeEqual<TValue>(
Expression<Func<TValue>> selector,
TValue expected)
{
TValue value = selector.Compile()();
string name = GetName(selector.Body);
System.Diagnostics.Debug.Assert(
EqualityComparer<TValue>.Default.Equals(value, expected),
typeof(TValue) + " " + name + ": " +
value + " doesn't match expected " + expected);
}
}
public static void ShouldBe<T>(this T actual, T expected)
var frame = new StackTrace(true).GetFrame(1);
var fileName = frame.GetFileName();
var lineNumber = frame.GetFileLineNumber() - 1;
string code = File.ReadLines(fileName).ElementAt(lineNumber).Trim();
Debug.Assert(actual.Equals(expected), code);