How to reference the type of a private class from an assembly-level attribute?
-
16-01-2021 - |
Вопрос
I have defined an assembly level attribute class FooAttribute
like this:
namespace Bar
{
[System.AttributeUsage (System.AttributeTargets.Assembly, AllowMultiple=true)]
public sealed class FooAttribute : System.Attribute
{
public FooAttribute(string id, System.Type type)
{
// ...
}
}
}
and I use it to associate an id to classes, for instance:
[assembly: Bar.Foo ("MyClass", typeof (Bar.MyClass))]
namespace Bar
{
public class MyClass
{
private class Mystery { }
}
}
This all works fine. But what if I need to somehow reference the private class Mystery
, defined in MyClass
? Is this at all possible? Trying to reference it from the top-level [assembly: ...]
directive does not work, as the type is not publicly visible:
[assembly: Bar.Foo ("Mystery", typeof (Bar.MyClass.Mystery))] // won't work
And trying to put the [assembly: ...]
directive into MyClass
in so that it could see Mystery
is not legal, as [assembly: ...]
must be defined at the top level:
namespace Bar
{
class MyClass
{
[assembly: FooAttribute (...)] // won't work either
...
}
}
There is a way to access internal
types from outside of an assembly by declaring the user a friend of the assembly, but how about referencing private types inside an assembly? I guess it is not possible, and I just would have to declare Mystery
to be internal
instead, but I want to be sure I did not miss some subtlety.
Решение
Your assertions in your last paragraphs are correct. Your options would be to:
- Make the nested class internal to enable
typeof
or
- Have an added constructor to
FooAttribute
which takes the fully qualified type name of the private nested class, and then uses reflection to get aSystem.Type
representing it.
For instance:
public sealed class FooAttribute
{
public FooAttribute(string id, string typeName)
{
var type = Type.GetType(typeName);
// whatever the other ctor does with the System.Type...
}
}
usage:
[assembly: Foo("Bar", typeof(Bar))]
[assembly: Foo("Baz", "Foo.Bar+Baz, MyAssembly")]
namespace Foo
{
public class Bar
{
private class Baz
{
}
}
}
Другие советы
Making it internal
(which you already state you don't want to do) is the least effort approach. For the majority of code, allowing MyClass
to expose (via a static property) the type instance (i.e. public static Type MysteryType { get { return typeof(Mystery); } }
would work, but that won't work from an attribute (only constant values of a few basic types can be used).
The only alternative to internal
, then, is to code it as a string literal, (i.e. [Foo("Bar.MyClass+Mystery")]
) and use typeof(MyClass).Assembly.GetType(fullName)
- but then you lose the compiler validation that typeof
normally provides. (note also the +
that the runtime uses to represent nested types, not .
which is the C# representation)
Personally, I'd just make it internal
.