Custom attribute with a Type constructor parameter

78 views
Skip to first unread message

Sebastien

unread,
Apr 27, 2009, 12:12:14 PM4/27/09
to mono-cecil

Hi,

I'm adding a field to an assembly with a custom attribute. This custom
attribute takes a Type as a constructor parameter. Inspecting the
generated assembly shows that the actual parameter for the constructor
is set to nullref.

Sample of the code that I used:

[...]
var ca = new CustomAttribute(ctor);
var stringType = asm.MainModule.Import(typeof(string));
ca.ConstructorParameters.Add(stringType);
var field = new FieldDefinition("Test", stringType,
FieldAttributes.Public);
field.CustomAttributes.Add(ca);
[...]

The ctor for the custom attribute looks like this:
public TestAttribute(Type t) { ...}

Inspecting the generated assembly shows the following:

.field public string Test
{
.custom instance void TestAttribute::.ctor(class [mscorlib]
System.Type) = { (nullref) }
}

I did some debugging and noticed the following lines in
SignatureWriter.Write(CustomAttrib,MemoryBinaryWriter)

case ElementType.Type :
string s = elem.Value as string;
if (s == null)
writer.Write ((byte) 0xff);

I tried to replace "elem.Value as string" by elem.Value.ToString(),
and the custom attribute is now properly generated. (I'm not
suggesting a fix, I'm just trying to trace down the problem.)

(... in case of a Type constructor parameter,
ReflectionWriter.CreateElem assign CustomAttrib.Elem.Value to a
TypeReference and not to a string...)

Am I missing something or is this a bug ?

Thanks,
Sebastien

Sebastien

unread,
Apr 27, 2009, 12:58:29 PM4/27/09
to mono-cecil

> I'm adding a field to an assembly with a custom attribute.

Oops, this should read, "I'm adding a field with a custom attribute to
a Type"...

Keith

unread,
Apr 27, 2009, 6:41:16 PM4/27/09
to mono-cecil
Hey Sebastien,

Cecil is quite close to the metadata "under the hood." That is, the
way you represent things in Cecil objects is very close to how they're
represented in the physical metadata. This is both good and bad. You
have excellent control over what's in the metadata from "object land,"
but you sacrifice many high level niceties.

The relevant nicety here is representing a Type as a Type. According
to the Standard:
<quote source="CLI Standard v4, Partition II, 23.3">
If the parameter kind is System.Type, ... its value is stored as a
SerString (as defined in the previous paragraph), representing its
canonical name. The canonical name is its full type name, followed
optionally by the assembly where it is defined, its version, culture
and public-key-token.
</quote>
This is why ToString() on the Type works; it returns the full type
name.

The easy solution for you is to use the full type name, instead of a
Type or TypeDefinition object, as the parameter value of your
CustomAttribute. You could also build out a full "canonical name" if
you wish. The Specification doesn't do a good job specifying the EXACT
format, but this shows it by example for a non-generic type. It's for
the EditorAttribute from System.Drawing.Image:

'System.Drawing.Design.ImageEditor, System.Drawing.Design,
Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a'

(I hope you don't need a generic type as an attribute parameter. The
Standard is silent on that, far as I can find, and I haven't found an
example to follow.)

Hope that helps,
-- Keith

Jb Evain

unread,
Apr 28, 2009, 2:28:45 AM4/28/09
to mono-...@googlegroups.com
Hey Keith,

On 4/28/09, Keith <ke...@kuler.com> wrote:
> Cecil is quite close to the metadata "under the hood." That is, the
> way you represent things in Cecil objects is very close to how they're
> represented in the physical metadata. This is both good and bad. You
> have excellent control over what's in the metadata from "object land,"
> but you sacrifice many high level niceties.

Well, in that case, I'll happily blame the way custom attributes are
encoded, which are tied to a System.Reflection way of describing
types.

The refactored Cecil will break API compatibility for custom
attributes, to better express typed values in custom attributes.

> (I hope you don't need a generic type as an attribute parameter. The
> Standard is silent on that, far as I can find, and I haven't found an
> example to follow.)

You just have to use a System.Reflection generic full name.

Foo[System.Int32, Version=2.0.0.0, ......], ....

--
Jb Evain <j...@nurv.fr>

Sebastien

unread,
Apr 28, 2009, 10:51:22 AM4/28/09
to mono-cecil


It worked indeed; thanks to both of you.

Sebastien.
Reply all
Reply to author
Forward
0 new messages