I am writing a kind of TLV de-/serialization class.
Like protobuf-net I have a contract attribut for classes and a member attribut for properties. The member attributes have a Tag number like in protobuf. Now I would like to check if a Tag number is already used, the best solution would be if there is some kind of compiler error for this. I also have postsharp if this will help. The class structure looks like this:
[TlvContract]
public class Person{
[TlvMember(1)]
public String Name{get; set;}
[TlvMember(2)]
public Int32 ID{get; set;}
// This should create a warning or compile error!!!!
[TlvMember(1)]
public String Town{get; set;}
}
Apart from Roslyn analyzers, PostSharp is a possible way to go.
Following is a basic implementation of such checks and error outputs:
[MulticastAttributeUsage(PersistMetaData = true)]
public class TlvContractAttribute : TypeLevelAspect
{
public override void CompileTimeInitialize(Type target, AspectInfo aspectInfo)
{
Dictionary<int, PropertyInfo> indexes = new Dictionary<int, PropertyInfo>();
foreach (PropertyInfo propertyInfo in target.GetProperties())
{
TlvMemberAttribute memberAttr =
propertyInfo.GetCustomAttributes()
.Where(x => x is TlvMemberAttribute)
.Cast<TlvMemberAttribute>()
.SingleOrDefault();
if (memberAttr == null)
{
Message.Write(MessageLocation.Of(propertyInfo), SeverityType.Error, "USR001",
"Property {0} should be marked by TlvMemberAttribute.", propertyInfo);
continue;
}
if (indexes.ContainsKey(memberAttr.Index))
{
Message.Write(MessageLocation.Of(propertyInfo), SeverityType.Error, "USR002",
"Property {0} marked by TlvMemberAttribute uses Index {1}, which is already used by property {2}.",
propertyInfo, memberAttr.Index, indexes[memberAttr.Index]);
continue;
}
indexes[memberAttr.Index] = propertyInfo;
}
}
}
It works in the assembly in which you define it. You just need to make sure that PostSharp actually runs on all assemblies where you want the check to work.
If you need a different base class for your attribute, you can also implement ITypeLevelAspect
and ITypeLevelAspectBuildSemantics
interfaces.