此问题末尾的代码在Source上具有GetHashCode()。匿名类型也是如此:编译器会生成类似的代码。
使用Crypto Obfuscator,当您指定重命名方法时,GetHashCode公共方法被重命名。结果是GroupBy返回了错误的元素:应该相等的元素被认为是不同的,因为它们引用了不同的对象,并且使用了默认的GetHashCode实现。
是否有一种明智的方法来禁用诸如GetHashCode()之类的基本方法上的Crypto Obfuscator重命名,尤其是当它们由编译器自动生成时?
还是我们必须完全停止使用匿名类型,将其替换为显式类型,并确保所有的GetHashCode和Equals方法都正确地使用了Obfuscation属性修饰?
using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
namespace Invantive.Basics
{
/// <summary>
/// Check obfuscation not too aggressive.
/// </summary>
[Obfuscation(ApplyToMembers=false, Exclude = true)]
public static class ObfuscationAsserter
{
private class Source
{
public string S1;
public string S2;
public Source(string s1, string s2)
{
this.S1 = s1;
this.S2 = s2;
}
public override int GetHashCode()
{
return 23 * this.S1.GetHashCode() + this.S2.GetHashCode();
}
public override bool Equals(object o)
{
if (o is Source s)
{
return s.S1 == this.S1 && s.S2 == this.S2;
}
else
{
return false;
}
}
}
/// <summary>
/// Check anonymous types etc. are correctly obfuscated.
/// </summary>
public static void Check()
{
List<Source> strings = new List<Source>();
strings.Add(new Source("a", "a"));
strings.Add(new Source("a", "b"));
strings.Add(new Source("a", "b"));
strings.Add(new Source("c", "c"));
int expected = 3;
int t1cnt = strings.GroupBy(x => new { x.S1, x.S2 }).ToList().Count();
int t2cnt = strings.GroupBy(x => new { x.S1, x.S2 }.GetHashCode()).ToList().Count();
int t3cnt = strings.GroupBy(x => x.S1 + x.S2).ToList().Count();
int t4cnt = strings.GroupBy(x => (x.S1 + x.S2).GetHashCode()).ToList().Count();
int t5cnt = strings.GroupBy(x => x).ToList().Count();
int t6cnt = strings.GroupBy(x => x.GetHashCode()).ToList().Count();
int t7cnt = strings.GroupBy(x => (x.S1, x.S2)).ToList().Count();
int t8cnt = strings.GroupBy(x => (x.S1, x.S2).GetHashCode()).ToList().Count();
InvantiveTrace.WriteLine($"t1: {t1cnt}, t2: {t2cnt}, t3: {t3cnt}, t4: {t4cnt}, t5: {t5cnt}, t6: {t6cnt}, t7: {t7cnt}, t8: {t8cnt}.");
if (t1cnt != expected)
{
throw new Exception($"t1 is {t1cnt:N0} versus expected {expected:N0}.");
}
if (t2cnt != expected)
{
throw new Exception($"t2 is {t2cnt:N0} versus expected {expected:N0}.");
}
if (t3cnt != expected)
{
throw new Exception($"t3 is {t3cnt:N0} versus expected {expected:N0}.");
}
if (t4cnt != expected)
{
throw new Exception($"t4 is {t4cnt:N0} versus expected {expected:N0}.");
}
if (t5cnt != expected)
{
throw new Exception($"t5 is {t5cnt:N0} versus expected {expected:N0}.");
}
if (t6cnt != expected)
{
throw new Exception($"t6 is {t6cnt:N0} versus expected {expected:N0}.");
}
if (t7cnt != expected)
{
throw new Exception($"t7 is {t7cnt:N0} versus expected {expected:N0}.");
}
if (t8cnt != expected)
{
throw new Exception($"t8 is {t8cnt:N0} versus expected {expected:N0}.");
}
}
}
}
运行良好:Check()不会引发异常。
W
和Z
已被GroupBy
使用,因此不应该引入它们:
private class D
{
public string J;
public string V;
public D(string text1, string text2)
{
this.J = text1;
this.V = text2;
}
public override int W() =>
((0x17 * this.J.GetHashCode()) + this.V.GetHashCode());
public override bool Z(object obj1)
{
ObfuscationAsserter.D d = obj1 as ObfuscationAsserter.D;
if (d == null)
{
return false;
}
return ((d.J == this.J) && (d.V == this.V));
}
}
这仍然可以正常工作:
private class Source
{
public string S1;
public string S2;
public Source(string s1, string s2)
{
this.S1 = s1;
this.S2 = s2;
}
public override bool Equals(object o)
{
ObfuscationAsserter.Source source = o as ObfuscationAsserter.Source;
if (source == null)
{
return false;
}
return ((source.S1 == this.S1) && (source.S2 == this.S2));
}
public override int GetHashCode() =>
((0x17 * this.S1.GetHashCode()) + this.S2.GetHashCode());
}
由于GetHashCode()不再存在,因此编译器生成的类具有相同的问题:
[Serializable, CompilerGenerated]
private sealed class <>c
{
public static Func<ObfuscationAsserter.D, int> E;
public static Func<ObfuscationAsserter.D, int> F;
public static Func<ObfuscationAsserter.D, ObfuscationAsserter.D> I;
public static readonly ObfuscationAsserter.<>c J = new ObfuscationAsserter.<>c();
public static Func<ObfuscationAsserter.D, J<string, string>> V;
public static Func<ObfuscationAsserter.D, int> W;
[TupleElementNames(new string[] { "S1", "S2" })]
public static Func<ObfuscationAsserter.D, (string S1, string S2)> X;
public static Func<ObfuscationAsserter.D, int> Y;
public static Func<ObfuscationAsserter.D, string> Z;
internal int B(ObfuscationAsserter.D d1)
{
(string, string) tuple = (d1.J, d1.V);
return tuple.GetHashCode();
}
[return: TupleElementNames(new string[] { "S1", "S2" })]
internal (string S1, string S2) L(ObfuscationAsserter.D d1) =>
(d1.J, d1.V);
internal int N(ObfuscationAsserter.D d1) =>
d1.GetHashCode();
internal string P(ObfuscationAsserter.D d1) =>
(d1.J + d1.V);
internal J<string, string> R(ObfuscationAsserter.D d1) =>
new J<string, string>(d1.J, d1.V);
internal int S(ObfuscationAsserter.D d1) =>
(d1.J + d1.V).GetHashCode();
internal int T(ObfuscationAsserter.D d1) =>
new J<string, string>(d1.J, d1.V).GetHashCode();
internal ObfuscationAsserter.D U(ObfuscationAsserter.D d1) =>
d1;
}