我需要比较C#委托的等价性。如果两个委托在对象(或静态)的相同实例上调用相同的方法,或者如果它们的方法体具有完全相同的编译后的IL,我认为两个委托是相等的。下面的代码包括我需要比较才能通过的测试用例:
using System;
namespace ConsoleApplication
{
public delegate int Compare<Type>(Type left, Type right);
public delegate int Compare<Left, Right>(Left left, Right right);
public class Program
{
public static void Main(string[] args)
{
// Test 0 (false control)
Action _0_1 = () => { };
Action<int> _0_2 = (int i) => { Math.Sign(i); };
Console.WriteLine("0:\t" + (Equate(_0_1, _0_2) == false));
// Test 1s (same type delegates from static-method)
Compare<int> _1s_1 = Test;
Compare<int> _1s_2 = Test;
Console.WriteLine("1s:\t" + Equate(_1s_1, _1s_2));
// Test 1i (same type delegates from instance-method)
Program _1i_0 = new Program();
Compare<int> _1i_1 = _1i_0.Test3;
Compare<int> _1i_2 = _1i_0.Test3;
Console.WriteLine("1i:\t" + Equate(_1i_1, _1i_2));
// Test 2s (same type delegates from same type static-delegates)
Compare<int> _2s_1 = new Compare<int>(_1s_1);
Compare<int> _2s_2 = new Compare<int>(_1s_2);
Console.WriteLine("2s:\t" + Equate(_2s_1, _2s_2));
// Test 2i (same type delegates from same type instance-delegates)
Compare<int> _2i_1 = new Compare<int>(_1i_1);
Compare<int> _2i_2 = new Compare<int>(_1i_2);
Console.WriteLine("2i:\t" + Equate(_2i_1, _2i_2));
// Test 3s (different type delegates from static-method)
Compare<int> _3s_1 = Test;
Compare<int, int> _3s_2 = Test;
Console.WriteLine("3s:\t" + Equate(_3s_1, _3s_2));
// Test 3i (different type delegates from instance-method)
Program _3i_0 = new Program();
Compare<int> _3i_1 = _3i_0.Test3;
Compare<int, int> _3i_2 = _3i_0.Test3;
Console.WriteLine("3i:\t" + Equate(_3i_1, _3i_2));
// Test 4s (same type delegates from different type static-delegates)
Compare<int> _4s_1 = new Compare<int>(_3s_1);
Compare<int> _4s_2 = new Compare<int>(_3s_2);
Console.WriteLine("4s:\t" + Equate(_4s_1, _4s_2));
// Test 4i (same type delegates from different type instance-delegates)
Compare<int> _4i_1 = new Compare<int>(_3i_1);
Compare<int> _4i_2 = new Compare<int>(_3i_2);
Console.WriteLine("4i:\t" + Equate(_4i_1, _4i_2));
// Test 4s.1 (same type delegates from different type static-delegates)
Compare<int, int> _4s_1_1 = new Compare<int, int>(_3s_1);
Compare<int, int> _4s_1_2 = new Compare<int, int>(_3s_2);
Console.WriteLine("4s.1:\t" + Equate(_4s_1_1, _4s_1_2));
// Test 4i.1 (same type delegates from different type instance-delegates)
Compare<int, int> _4i_1_1 = new Compare<int, int>(_3i_1);
Compare<int, int> _4i_1_2 = new Compare<int, int>(_3i_2);
Console.WriteLine("4i.1:\t" + Equate(_4i_1_1, _4i_1_2));
// Test 5s (same type delegates from different static-methods with same IL compilations)
Compare<int> _5s_1 = Test;
Compare<int> _5s_2 = Test2;
Console.WriteLine("5s:\t" + Equate(_5s_1, _5s_2));
// Test 5i (same type delegates from different instance-methods with same IL compilations)
Program _5i_0 = new Program();
Compare<int> _5i_1 = _5i_0.Test3;
Compare<int> _5i_2 = _5i_0.Test4;
Console.WriteLine("5i:\t" + Equate(_5i_1, _5i_2));
Console.WriteLine();
Console.WriteLine("Enter to close...");
Console.ReadLine();
}
public static int Test(int l, int r) { return 0; }
public static int Test2(int l, int r) { return 0; }
public int Test3(int l, int r) { return 0; }
public int Test4(int l, int r) { return 0; }
// FIX ME!-----------------------------------------------------
public static bool Equate(System.Delegate a, System.Delegate b)
{
// standard equality
if (a == b)
return true;
// null
if (a == null || b == null)
return false;
// compiled method body
if (a.Target != b.Target)
return false;
byte[] a_body = a.Method.GetMethodBody().GetILAsByteArray();
byte[] b_body = b.Method.GetMethodBody().GetILAsByteArray();
if (a_body.Length != b_body.Length)
return false;
for (int i = 0; i < a_body.Length; i++)
{
if (a_body[i] != b_body[i])
return false;
}
return true;
}
}
}以下是当前失败的测试: 2s、2i、4s、4i、4s.1、4i.1
发布于 2015-06-18 02:17:52
以下是这些测试用例的解决方案。你必须消除所有由委托赋值引起的开销。只要不断检查目标是否为委托即可。
public static bool Equate(System.Delegate a, System.Delegate b)
{
// ADDED THIS --------------
// remove delegate overhead
while (a.Target is Delegate)
a = a.Target as Delegate;
while (b.Target is Delegate)
b = b.Target as Delegate;
// standard equality
if (a == b)
return true;
// null
if (a == null || b == null)
return false;
// compiled method body
if (a.Target != b.Target)
return false;
byte[] a_body = a.Method.GetMethodBody().GetILAsByteArray();
byte[] b_body = b.Method.GetMethodBody().GetILAsByteArray();
if (a_body.Length != b_body.Length)
return false;
for (int i = 0; i < a_body.Length; i++)
{
if (a_body[i] != b_body[i])
return false;
}
return true;
}发布于 2015-06-18 02:22:48
因此,让我们拿出第一个失败的测试用例,并孤立地查看它。为了清楚起见,我将更改几个变量名。
Compare<int> firstComparer = Test;
Compare<int> secondComparer = Test;
Compare<int> thirdComparer = new Compare<int>(firstComparer);
Compare<int> fourthComparer = new Compare<int>(secondComparer);
Console.WriteLine("2s:\t" + Equate(thirdComparer, fourthComparer));现在,当我们进行比较时,让我们看看这四个委托各自的目标和方法:
variable | Target | Method
firstComparer | null | Test
secondComparer | null | Test
thirdComparer | firstComparer | Invoke
fourthComparer | secondComparer | Invoke现在,从技术上讲,thirdComparer的Target不是变量firstComparer,目标是该变量的值;它是firstComparer在计算new Compare<int>(firstComparer)时指向的委托,但希望您能理解。
那么为什么第三个和第四个委托不相等呢?因为他们有完全不同的目标。这两个委托都在调用两个不同实例的相同方法。在这种情况下,当你调用这两个完全不同的实例时,它们会做同样的事情,但不一定是这样的。
因此,如果您希望您的等式支持这一点,那么您需要以某种方式确定不同委托的目标是指向相同的变量还是指向相等的值。编写适用于这种情况的内容可能是可行的,而编写涵盖一般情况的内容可能是不可能的。在一般情况下,你不一定知道两个对象是否应该被认为是“等价的”。如果该实现足够的话,您可以拉出一个IEqualityComparer<T>.Default。它可能会在这里工作,但并不是所有的类型都以您希望的方式覆盖相等。
最好的解决方案可能是从一开始就避免这个问题。而不是让委托,当调用时,调用其他不同的委托,但本身指向相同的值,您应该删除该间接层。如果我们将上面的测试用例调整为:
Compare<int> firstComparer = Test;
Compare<int> secondComparer = Test;
Compare<int> thirdComparer = firstComparer;
Compare<int> fourthComparer = secondComparer;
Console.WriteLine("2s:\t" + Equate(thirdComparer, fourthComparer));然后测试通过,因为第三个和第四个委托彼此不同,但它们是firstComparer和secondComparer指向的相同委托,这两个委托都具有相同的目标和方法。
您的所有其他测试用例,而不仅仅是这一个测试用例,都有完全相同的问题,所以我认为没有理由单独查看每个测试用例。它们都在增加这种间接层,这就是它们不相等的原因。
https://stackoverflow.com/questions/30898428
复制相似问题