[mono-cecil] Suggestion to implement a TypeReference.Equals()

475 views
Skip to first unread message

Timwi

unread,
May 17, 2010, 3:07:48 AM5/17/10
to mono-cecil
I have run into a case where I have two separate TypeReference objects
that actually refer to the same constructed type (namely,
List<string>). Would it be advisable to override the .Equals() method
so that they would compare as equal?

--
--
mono-cecil

Jan

unread,
May 18, 2010, 3:37:41 AM5/18/10
to mono-cecil
+1

And the same for FieldReference, I have a lot of comparisons between
field access and field defitinion.

Maybe there is a common way to implement it.

Jan
--
--
mono-cecil

Jb Evain

unread,
May 18, 2010, 6:38:23 AM5/18/10
to mono-...@googlegroups.com
Thing is that there's a lot of things that could be tricky. So I'd
rather provide an EqualityComparer for MemberReference and
TypeReference in Mono.Cecil.Rocks, than override Equals and
GetHashCode is the object model.
--
Jb Evain <j...@nurv.fr>

--
--
mono-cecil

Timwi

unread,
May 18, 2010, 11:05:59 AM5/18/10
to mono-cecil
I'm not sure I follow your reasoning. Surely whether it's tricky or
not is completely independent of whether it's in Rocks or directly in
Equals?
--
--
mono-cecil

Jb Evain

unread,
May 18, 2010, 11:30:26 AM5/18/10
to mono-...@googlegroups.com
On Tue, May 18, 2010 at 5:05 PM, Timwi <ti...@gmx.net> wrote:
> I'm not sure I follow your reasoning. Surely whether it's tricky or
> not is completely independent of whether it's in Rocks or directly in
> Equals?

Not only it's tricky, but one can expect different behavior. Should a
MethodReference be Equals to another MethodReference when they are
defined in different module ? Most of the time, yes, in some other
places, I'd rather not.

Another big issue is with generic parameters, should they be compared
by position or by identity when used as an argument.

So for things like that, I'd rather have the code in Mono.Cecil.Rocks,
and experiment there. If at some point, everyone is happy with it,
we'll move it to Cecil. But it's nice to have Mono.Cecil.Rocks as a
playground.

Timwi

unread,
May 18, 2010, 11:37:01 AM5/18/10
to mono-cecil
I see. That makes sense, actually. Thanks for explaining.
--
--
mono-cecil

Timwi

unread,
May 22, 2010, 12:50:44 PM5/22/10
to mono-cecil

Any luck with this yet? I’m running into this more and more now, and
I’m finding it increasingly harder to do it myself. Right now I have
two references to System.String — one from my input assembly and one
from mscorlib — and I can’t really find a way to compare them in such
a way that I won’t accidentally confuse two types that only happen to
have the same name. The TypeReference.Scope property refers to
mscorlib version 4.0 in one case, and version 4.0.0.0 in the other,
and using .Equals() on either the IMetadataScope objects or the
Version objects keeps returning false. And .Resolve() also returns me
separate instances of TypeDefinition, so they also compare as
different. What other options do I have?

On May 18, 4:30 pm, Jb Evain <j...@nurv.fr> wrote:
--
--
mono-cecil

Jb Evain

unread,
May 24, 2010, 6:16:42 AM5/24/10
to mono-...@googlegroups.com
Hey,

On Sat, May 22, 2010 at 6:50 PM, Timwi <ti...@gmx.net> wrote:
> The TypeReference.Scope property refers to
> mscorlib version 4.0 in one case, and version 4.0.0.0 in the other

That should not happen anymore with the latest versions. If you find a
case where the corlib has a 4.0 version instead of 4.0.0.0, please
post a repro.

> What other options do I have?

It's not really hard, to compare two type references, you can first
compare its namespace and its name. If they're not equal, the types
are different. If they're equal, you need to compare their assembly.
If the Scope is a ModuleDefinition, then you compare its
module.Assembly.Name.FullName, if it's an AssemblyNameReference, you
can just use its FullName.

Timwi

unread,
May 24, 2010, 6:52:54 AM5/24/10
to mono-cecil
Many thanks once again! :)
--
--
mono-cecil

Daniel Grunwald

unread,
May 24, 2010, 7:28:27 AM5/24/10
to mono-...@googlegroups.com
On 24.05.2010 12:16, Jb Evain wrote:
> It's not really hard, to compare two type references, you can first
> compare its namespace and its name. If they're not equal, the types
> are different. If they're equal, you need to compare their assembly.
> If the Scope is a ModuleDefinition, then you compare its
> module.Assembly.Name.FullName, if it's an AssemblyNameReference, you
> can just use its FullName.
>
That's only a rough approximation.
In .NET 4 due to the [TypeForwardedTo]-attribute, two types can be equal
even if they are in different assemblies (for example, the delegate
System.Action in System.Core, version=3.5.0.0 is considered the same
type as System.Action in mscorlib, version=4.0.0.0).
Also, two assemblies with the same simple name but different versions
may or may not be equal depending on runtime policies (version
redirection etc.).
In general, a static analysis tool such as Mono.Cecil can never be sure
whether two types are equal.

Some possible solutions:
1) Only use Namespace+Type Name: you'll have lots of false positives
(almost all BCL assemblies share internal classes in the internal MS.*
namespaces)
2) Use Namespace+Type Name+Simple Assembly Name: you'll have both false
positives (rare, only if app loads different versions of the same
assembly) and false negatives (redirected types)
3) Use Namespace+Type Name+Full Assembly Name: you'll have false
negatives (redirected types + assemblies redirected to another version)

I think best would be 2), maybe you could also add some special code to
detect [TypeForwardedTo].
Note that both redirected types and redirected assembly versions are
commonly used (e.g. when using .NET 2.0 or 3.x assemblies in a .NET 4.0
app).

Daniel

signature.asc
Reply all
Reply to author
Forward
0 new messages