Reputation: 6383
EDIT: looks like it's not possible to have only one attribute with everything inside like solution #2, so I ended up using solution #3 which is not error prone unlike solution #1.
I have an attribute as follow:
// Solution #1 (working)
[Module(Name="Module1", Guid="xxxxx",
NeededFiles_Name = new string[]
{
"module1Conf.xml",
"module1.sql",
"resourcesWindows",
"resourcesUnix",
},
NeededFiles_OS = new OSType[]
{
All,
All,
Windows,
Unix,
}
NeededFiles_Component = new ComponentType[]
{
Server,
Server,
All,
All
}
)]
The problem is that it's not easy to fill the NeededFile_Xxx properties. It would be easier to have only one property like
// Solution #2 (NOT working)
[Module(Name="Module1", Guid="xxxxx",
NeededFile = new FileInfo[]
{
new FileInfo("module1Conf.xml", All, Server),
...
}
)]
but it's not allowed by the compiler (NeededFile is not a valid named attribute argument because it is not a valid attribute parameter type.
I could also divide my attribute into severals and do
// Solution #3 (working)
[Module(Name="Module1", Guid="xxxxx"]
[NeededFile("module1Conf.xml", All, Server)]
[NeededFile("module1.sql", All, Server)]
[NeededFile(...)]
but I'd rather keep everything in the same.
Is it possible to do it? Is there a better way to do it?
Upvotes: 3
Views: 4343
Reputation: 4802
Why isn't it (you second option) allowed? I just tried the following:
public class MyFileInfo
{
public MyFileInfo(string fname, string somethingElse, string anotherThing);
}
public class ModuleAttribute : System.Attribute
{
public ModuleAttribute(string Name, string Guid, params MyFileInfo[] myFileInfoList)
{
}
}
[Module("Module1", "xxxx",
new MyFileInfo("a", "b", "c"),
new MyFileInfo("a", "d", "f"))
]
public class TestClass
{
}
And the compiler didn't complain. I think you can achieve pretty much exactly what you're trying to do.
Edit: that's what I get for letting intellisense do my compiling for me in the middle of the night after a couple beers. As you (and I) have discovered, Attribute arguments must be constants, or lists of constants - a restriction that explicitly excludes classes and structs.
I think the best you're going to be able to do is pass in a serialized version of the data you're trying to tag onto the attribute. Then the attribute constructor can do the actual object initialization at runtime. This loses compile-time checking of your initializers, but gets you a bit closer to the compactness of code you were looking for.
public class MyFileInfo
{
string fname;
string anotherThing;
string somethingElse;
public MyFileInfo(string serializedFileInfo)
{
string[] parts = serializedFileInfo.Split(',');
fname = parts[0];
anotherThing = parts[1];
somethingElse = parts[2];
}
public static implicit operator MyFileInfo(string things)
{
return new MyFileInfo(things);
}
}
public class ModuleAttribute : System.Attribute
{
List<MyFileInfo> fiList;
public ModuleAttribute(string Name, string Guid, params string[] serializedFileInfoList)
{
fiList = serializedFileInfoList.Select(s => new MyFileInfo(s)).ToList();
}
}
[Module("Module1", "xxxx",
"a,b,c",
"d,e,f"
)
]
public class TestClass
{
}
Really though, I think you should just handle it through a separate attribute for each file.
Upvotes: 7