C# Automatic find a Children Class by a Unique ID - c#

I have about 20 classes for different messages and this number are growing. Each class has a unique ID, so I can transform the class on a byte[] with my own method of serialization and then transform a byte[] again on my class with this uniqueID.
All my messages are children of a BaseMessage class that already implements the uniqueID generation correctly.
What I want to do is direct find the class of a respective ID without using a Enum to compare.
My problem with the Enum is that Enums are not auto updated with my new IDs every time I create a new message class.
There a way to combine Attributes and Assembly to do this, Like discovering all children of BaseClass and then call for a CustomAtributte?
Thank you!

You're on the right path - that does sound like a good way to handle it. You'll want to store the unique ID of the type alongside the serialized value, so you can read the ID before deserialization, to direct the deserializer to the correct type. You could also just store the fully qualified type name instead of using an ID, but this is a fine approach too.
The attribute is simple enough to create:
class MessageAttribute : Attribute
{
public Guid ID; //assuming you want to use a GUID, could be anything
}
And using it is also simple:
[Message(ID = new Guid("..."))]
class SubMessage : BaseMessage
{
}
You can load all the types in a given assembly and iterate over them very quickly:
List<Type> messageTypes = new List<Type>();
//get the assembly containing our types
Assembly messageAssembly = Assembly.Load(...);
//get all the types in the assembly
Type[] types = messageAssembly.GetTypes();
foreach(Type type in types)
{
//make sure we inherit from BaseMessage
if(type.IsAssignableFrom(typeof(BaseMessage))
{
//check to see if the inherited type has a MessageAttribute
object[] attribs = type.GetCustomAttribtues(typeof(MessageAttribute), true);
if(attribs.Length > 0)
{
messageTypes.Add(type);
}
}
}

Related

Mapping Enum values to Classes

I want to be able to map each value of an Enum to a different class (and the reverse), but keep the relationships stored in a single place so they can be altered/added to without making changes in multiple location.
What pattern would allow me map Enum values to Classes?
My specific usage is roughly as follows:
I have a Person class that holds general information about the person. To this class can be added components (stored in a List Dictionary) that hold information specific to a particular type of person, eg. componentCustomer, componentEndUser, componentSupplier. These component-classes implement an IPersonComponent interface. Importantly, a person can be more than one type and can therefore hold more than one component.
I also have an Enum ePersonType with values eg. Customer, End User, Supplier. Each value has a one-to-one relationship with a component.
When a person's record is retrieved, a List<int> is populated with the appropriate 'ePersonType' values. This list is then used to determine what components need to be allocated to the Person and loaded with data.
Clearly, it is straightforward to switch on the person's types and add components appropriately. However, what if I later want to check what components the person instance holds and what ePersonTypes they are? I could switch back on component.GetType(), but then I am storing the relationship in two places.
Alternatively, I could store the appropriate ePersonType inside each component and use Linq to check whether an person instance has a particular component, but then it seems like the original allocation would be more complex, possibly requiring reflection?
I feel sure that I'm missing something really obvious here.
One of the ways I've approached this is to use an attribute on the actual enum values:
public enum PersonType
{
[PersonClass(typeof(Customer)]
Customer,
[PersonClass(typeof(EndUser)]
EndUser,
[PersonClass(typeof(Supplier)]
Supplier,
}
public class PersonClassAttribute : System.Attribute
{
public Type Type { get; }
public PersonClassAttribute(Type type)
{
Type = type;
}
}
Then use a static class for managing the retrieval and mapping of enums to types:
public static class People
{
static Dictionary<PersonType, Type> mapping = new Dictionary<PersonType, Type>();
static People()
{
var fields = Enum.GetNames(typeof(PersonType)).Select(n => typeof(PersonType).GetFieldr(n));
mapping = fields.ToDictionary(
f => (PersonType)f.GetRawConstantValue(),
f => f.GetCustomAttribute<PersonClassAttribute>().Type
);
}
public static T GetPersonInstance<T>(this PersonType type)
{
return (T)Activator.CreateInstance(mapping[type]);
}
}
Obviously there'd be a bit more to it (validation, error checking, etc), but that's the general idea.

Can I deserialize generics without a reference to the type?

I am attempting to save/load a class to an xml file that contains generic types using a DataContractSerializer. I have the save working, but have realized I can't load it because I don't have the list of knownTypes for the deserializer.
Is there a way of serializing/deserializing this class that would allow me to deserialize it without referencing any of the stored types directly?
Here is my SessionVariables class that I am trying to save/load:
[DataContract]
public class SessionVariables
{
[DataMember]
private Dictionary<Type, ISessionVariables> _sessionVariables = new Dictionary<Type, ISessionVariables>();
private object _syncLock = new object();
public T Get<T>()
where T : ISessionVariables, new()
{
lock (_syncLock)
{
ISessionVariables vars = null;
if (_sessionVariables.TryGetValue(typeof(T), out vars))
return (T)vars;
vars = new T();
_sessionVariables.Add(typeof(T), vars);
return (T)vars;
}
}
public IList<Type> GetKnownTypes()
{
IList<Type> knownTypes = new List<Type>();
knownTypes.Add(this.GetType().GetType()); // adds System.RuntimeType
foreach (Type t in _sessionVariables.Keys)
{
if (!knownTypes.Contains(t))
knownTypes.Add(t);
}
return knownTypes;
}
}
The different modules of the application extend the ISessionVariables interface to create their own set of session variables, like this:
[DataContract]
public class ModuleASessionVariables : ISessionVariables
{
[DataMember]
public string ModuleA_Property1{ get; set; }
[DataMember]
public string ModuleA_Property2 { get; set; }
}
[DataContract]
public class ModuleBSessionVariables : ISessionVariables
{
[DataMember]
public string ModuleB_Property1{ get; set; }
[DataMember]
public string ModuleB_Property2 { get; set; }
}
And a singleton instance of the SessionVariables class is used to access session variables, like this:
singletonSessionVariables.Get<ModuleASessionVariables>().ModuleA_Property1
singletonSessionVariables.Get<ModuleBSessionVariables>().ModuleB_Property2
I got the save working like this:
using (FileStream writer = new FileStream(#"C:\test.txt", FileMode.Create))
{
DataContractSerializer dcs = new DataContractSerializer(typeof(SessionVariables), singletonSessionVariables.GetKnownTypes());
dcs.WriteObject(writer, singletonSessionVariables);
writer.Close();
}
However this method does not work to deserialize the class because I don't know it's known types.
Can I serialize and deserialize generic types when I don't have direct library references to any of the types used? And if so, how?
The problem here is that you aren't just wanting to serialize data, but you also want to serialize data about your data, i.e., (cue the dramatic chipmunk) metadata.
That metadata, in this case, are the types of the models that held the data originally. Normally, this isn't an issue, but as you've discovered if you're taking advantage of polymorphism in your design, your single collection may contain two or more different types, each of which needs to be deserialized to their original type.
This is usually accomplished by saving this Type metadata to the serialized result. Different serialization methods does this in different ways. Xaml serialization uses xml namespaces associated with .net namespaces, then names the elements after the original type name. Json.net accomplishes this via a specific named value saved to the json object.
The default DataContractSerializer is not Type aware. Therefore you need to replace it with a version that understands the .NET Type system and can serialize/deserialize Type metadata to the resulting xml. Luckily, one already exists in the framework, the NetDataContractSerializer.
And that's how you pad a link-only answer. The Aristocrats.
You could accomplish this using a custom DataContractResolver. This allows you to plug into the deserialization pipeline and provide a type to deserialize into based upon the type/namespace that is found in the serialized graph.
Here's a good article on it:
http://blogs.msdn.com/b/carlosfigueira/archive/2011/09/21/wcf-extensibility-data-contract-resolver.aspx
IDesign has an implementation of a resolver that can be used for dynamic discovery of types on their site: http://idesign.net/Downloads/GetDownload/1848 (you will probably have to make some modifications to handle generics)

Deserializing missing type with protobuf.net

I have an application with plugin architecture. The plugins have their own data containers, which all inherit from the base interface IPluginDataStorage. The DataStorage object, which is serialized to preserve state, contains a list of these subclasses along with other data. The DynamicType flag is set to true, as it's not known until run time which plugins are in use.
[Serializable]
[ProtoContract]
public class DataStorage
{
[ProtoMember(200, DynamicType = true)]
public List<IPluginDataContainer> PluginDataStorage { get; set; }
When serializing this setup works okay, but I'm having problems deserializing the list reliably. If I try to deserialize the object without having access to all those plugins that were used when it was serialized, I, naturally, get an exception about a missing type.
Unable to resolve type: NOSMemoryConsumptionPlugin.NOSMemoryConsumptionData, NOSMemoryConsumptionPlugin, Version=1.2.0.17249, Culture=neutral, PublicKeyToken=null (you can use the TypeModel.DynamicTypeFormatting event to provide a custom mapping)
The exception gives a hint that I could provide the format through the event, but I don't see how that could be helpful as the problem is that I don't have that type available. What I'd like to do in these cases is to completely ignore deserializing that object. The list item could even default to a dummy instance of the base class in these cases. But how to do this?
That (choosing to skip or default) is a fascinating use-case that I don't think I have considered fully; however, you can probably do this yourself, via:
public class NilContainer : IPluginDataContainer {}
and then subscribe to the DynamicTypeFormatting event; if you don't recognise the type, supply typeof(NilContainer).
i.e.
RuntimeTypeModel.Default.DynamicTypeFormatting += (sender, args) =>
{
Type type;
if(!yourTypeMap.TryGetValue(args.FormattedName, out type))
{
type = typeof (NilContainer);
}
args.Type = type;
};
(completely untested)

Retrieving static members of multiple classes programmatically

I am not sure the best approach to this problem, be it through reflection, redesigning my classes altogether, or doing something simple.
Basically I have a base class, and I can have any number of subclasses which inherit from it. Let's call the base class Shape and the subclasses CircleShape, RectangleShape, etc.
The base class is never itself instantiated, only the subclasses. Some are never instatiated, some are instantiated many times throughout the life of the program.
Sometimes I need information specific to a subclass before I instantiate it. Right now I use an enum to differentiate all subclass types. And I instantiate each subclass based on the enum in a switch statement, like this:
switch (shapeType)
{
case CircleShape:
shape = new CircleShape();
case SquareShape:
shape = new RectangleShape();
}
But say instead of having to use this kind of hardcoded switch statement, I wanted to enumerate through all the subclasses. Is there a way to automatically retrieve a list of subclasses and access their STATIC members for info about them (before instantiating them)? Or is it easier to manually instantiate each class once and add them to an array so an I enumerate through them (but not tie those instances to any actual data).
Or should I do something completely different?
You can use attributes to define metadata on your classes and then use reflection to read this metadata at runtime to decide what you want to do with this class without having to instantiate it.
Here's some information on using attributes (you can create your own custom attributes too) using attributes in C#
Here's a quick sample of what this would look like:
Class Defenition:
// ********* assign the attributes to the class ********
[BugFixAttribute(121,"Jesse Liberty","01/03/05")]
[BugFixAttribute(107,"Jesse Liberty","01/04/05", Comment="Fixed off by one errors")]
public class MyMath
{
...
Using Reflection to read the attributes:
// get the member information and use it to retrieve the custom attributes
System.Reflection.MemberInfo inf = typeof(MyMath);
object[] attributes;
attributes = inf.GetCustomAttributes(typeof(BugFixAttribute), false);
// iterate through the attributes, retrieving the properties
foreach(Object attribute in attributes)
{
BugFixAttribute bfa = (BugFixAttribute) attribute;
Console.WriteLine("\nBugID: {0}", bfa.BugID);
Console.WriteLine("Programmer: {0}", bfa.Programmer);
Console.WriteLine("Date: {0}", bfa.Date);
Console.WriteLine("Comment: {0}", bfa.Comment);
}
NOTE: Be careful with using reflection too heavily on large numbers of iterations of large number of objects though, since it comes with a significant performance cost.
You could use reflection to enumerate all your classes, but this is not a very efficient way to do things since it is kind of slow.
If they are all in the same assembly you could do something like:
class Shape
{
/* ... */
}
class CircleShape : Shape
{
public static string Name
{
get
{
return "Circle";
}
}
}
class RectangleShape : Shape
{
public static string Name
{
get
{
return "Rectangle";
}
}
}
class Program
{
static void Main(string[] args)
{
var subclasses = Assembly.GetExecutingAssembly().GetTypes().Where(type => type.IsSubclassOf(typeof(Shape)));
foreach (var subclass in subclasses)
{
var nameProperty = subclass.GetProperty("Name", BindingFlags.Public | BindingFlags.Static);
if (nameProperty != null)
{
Console.WriteLine("Type {0} has name {1}.", subclass.Name, nameProperty.GetValue(null, null));
}
}
}
}
Of course you could also use attributes instead of static members which would probably preferable if you want to decorate the classes with information that you wanted to look up at runtime. There are many examples of how attributes work around the internet.

Getting custom attributes that were set outside the class

Suppose I have a class named Data.
Another class annotates one of its members, of type data, with some attribute.
For example:
public class Example{
[DefaultNameAttribute("default name")]
public Data Name{get;set}
}
What I'm looking for is a way, from within the class Data, to retrieve that attribute and the data it contains. I want to be able to write the following code:
public class Data{
private string _name = null;
public string Name{
get{
if (_name != null) return _name;
return (getDefaultNameFromAnnotation(this));//this is the method I'm looking for
}
}
In other words, I want to be able to give a default value to a specific field using custom attributes specified outside my class.
Short answer : You can't, or at least, you should not.
The DefaultNameAttribute is applied on a member of Example type, which is decorrelated from Data type. There could be several types that use Data type, and several instances of the DefaultNameAttribute. Example can even be defined in another assembly, etc.
It will depend on how your attribute is used (if it is on a class, property, method, etc...). For example if it is only used on a class you could get all types that are marked with your attribute with the following code:
public IEnumerable<Type> GetTypes(Assembly assembly)
{
foreach(Type type in assembly.GetTypes())
{
if (type.GetCustomAttributes(typeof(DefaultNameAttribute), true).Length > 0)
{
yield return type;
}
}
}
If it is only used on properties (as your example), then you could nest an additional foreach statement that will enumerate all properties for a given type and look for the attribute.

Categories