[c#] 구조를 비교할 때이 어설 션이 형식 예외를 던지는 이유는 무엇입니까?

System.Drawing.Size구조 의 동등성을 주장하려고하는데 예상되는 주장 실패 대신 형식 예외가 발생합니다.

[TestMethod]
public void AssertStructs()
{
    var struct1 = new Size(0, 0);
    var struct2 = new Size(1, 1);

    //This throws a format exception, "System.FormatException: Input string was not in a correct format."
    Assert.AreEqual(struct1, struct2, "Failed. Expected {0}, actually it is {1}", struct1, struct2); 

    //This assert fails properly, "Failed. Expected {Width=0, Height=0}, actually it is {Width=1, Height=1}".
    Assert.AreEqual(struct1, struct2, "Failed. Expected " + struct1 + ", actually it is " + struct2); 
}

의도 된 동작입니까? 내가 여기서 뭔가 잘못하고 있니?



답변

나는 그것을있어. 그리고 네, 그것은 버그입니다.

문제는 여기에 두 가지 수준이 string.Format있다는 것입니다.

번째 수준의 서식은 다음과 같습니다.

string template  = string.Format("Expected: {0}; Actual: {1}; Message: {2}",
                                 expected, actual, message);

그런 다음 string.Format제공 한 매개 변수를 사용 합니다.

string finalMessage = string.Format(template, parameters);

(분명히 문화가 제공되고 어떤 종류의 위생 처리가 …하지만 충분하지 않습니다.)

예상 값과 실제 값이 문자열로 변환 된 후 중괄호로 끝나지 않는 한 괜찮아 보입니다 Size. 예를 들어 첫 번째 크기는 다음과 같이 변환됩니다.

{Width=0, Height=0}

따라서 두 번째 수준의 서식은 다음과 같습니다.

string.Format("Expected: {Width=0, Height=0}; Actual: {Width=1, Height=1 }; " +
              "Message = Failed expected {0} actually is {1}", struct1, struct2);

… 그게 실패한 것입니다. 아야.

사실, 우리는 예상되는 부분과 실제 부분에 우리의 매개 변수를 사용하도록 형식을 속여서 이것을 정말 쉽게 증명할 수 있습니다.

var x = "{0}";
var y = "{1}";
Assert.AreEqual<object>(x, y, "What a surprise!", "foo", "bar");

결과는 다음과 같습니다.

Assert.AreEqual failed. Expected:<foo>. Actual:<bar>. What a surprise!

우리가 예상 foo하지도 않았고 실제 가치도 아니었기 때문에 분명히 깨졌습니다 bar!

기본적으로 이것은 SQL 인젝션 공격과 비슷하지만 string.Format.

해결 방법 string.Format으로 StriplingWarrior가 제안한대로 사용할 수 있습니다 . 이렇게하면 실제 / 예상 값으로 서식을 지정한 결과에 대해 두 번째 수준의 서식이 수행되는 것을 방지 할 수 있습니다.


답변

버그를 찾은 것 같습니다.

이것은 작동합니다 (어설 션 예외 발생).

var a = 1;
var b = 2;
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

그리고 이것은 작동합니다 (메시지 출력).

var a = new{c=1};
var b = new{c=2};
Console.WriteLine(string.Format("Not equal {0} {1}", a, b));

그러나 이것은 작동하지 않습니다 (을 던졌습니다 FormatException).

var a = new{c=1};
var b = new{c=2};
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

나는 이것이 예상되는 행동이라는 이유를 생각할 수 없습니다. 버그 보고서를 제출하겠습니다. 그동안 해결 방법은 다음과 같습니다.

var a = new{c=1};
var b = new{c=2};
Assert.AreEqual(a, b, string.Format("Not equal {0} {1}", a, b));


답변

@StriplingWarrior에 동의합니다. 이것이 실제로 2 개 이상의 오버로드에서 Assert.AreEqual () 메서드의 버그 인 것처럼 보입니다. StiplingWarrior가 이미 지적했듯이 다음은 실패합니다.

var a = new { c = 1 };
var b = new { c = 2 };
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

나는 코드 사용법에 대해 좀 더 명시 적으로하기 위해 이것에 대해 약간의 실험을 해왔습니다. 다음도 작동하지 않습니다.

// specify variable data type rather than "var"...no effect, still fails
Size a = new Size(0, 0);
Size b = new Size(1, 1);
Assert.AreEqual(a, b, "Not equal {0} {1}", a, b);

// specify variable data type and name the type on the generic overload of AreEqual()...no effect, still fails
Size a = new Size(0, 0);
Size b = new Size(1, 1);
Assert.AreEqual<Size>(a, b, "Not equal {0} {1}", a, b);

이것은 나를 생각하게했다. System.Drawing.Size는 구조체입니다. 물체는 어떻습니까? param 목록 string 메시지 뒤의 목록 이 params object[]. 기술적으로 yes 구조체 객체이지만 특별한 종류 의 객체, 즉 값 유형입니다. 나는 이것이 버그가있는 곳이라고 생각한다. 와 유사한 사용법과 구조를 가진 자체 객체를 사용 Size하면 실제로 다음 작동합니다.

private class MyClass
{
    public MyClass(int width, int height)
        : base()
    { Width = width; Height = height; }

    public int Width { get; set; }
    public int Height { get; set; }
}

[TestMethod]
public void TestMethod1()
{
    var test1 = new MyClass(0, 0);
    var test2 = new MyClass(1, 1);
    Assert.AreEqual(test1, test2, "Show me A [{0}] and B [{1}]", test1, test2);
}


답변

첫 번째 주장이 틀렸다고 생각합니다.

대신 사용 :

Assert.AreEqual(struct1, 
                struct2, 
                string.Format("Failed expected {0} actually is {1}", struct1, struct2));


답변