Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

How to get type from enum?

Currently I have this working code

var content = JsonConvert.DeserializeObject<ClassOne>(message.Content);

How do I make the above code dynamic by replacing the hardcoded ClassOne with message.ContentCategory which stores an enum? How do I convert the enum value message.ContentCategory to a type?

public enum ContentCategory
{
    ClassOne, ClassTwo
}

I tried below but no success.

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

var content = JsonConvert.DeserializeObject<typeof(message.ContentCategory)>(message.Content);

>Solution :

The simplest solution would be a switch statement:

object content;
switch (message.ContentCategory)
{
    case ContentCategory.ClassOne:
        content = JsonConvert.DeserializeObject<ClassOne>(message.Content);
        break;
    case ContentCategory.ClassTwo:
        content = JsonConvert.DeserializeObject<ClassTwo>(message.Content);
        break;
    default:
        throw new ArgumentException($"Unknown category: {message.ContentCategory}", nameof(message));
}

Failing that, you need some way of determining which class (as a Type) the message actually belongs to. If you get that Type then you can use it like this:

Type targetType = DetermineType(message.ContentCategory);
object content = JsonConvert.DeserializeObject(message.Content, targetType);

This uses this non-generic overload of the DeserializeObject method.

DetermineType could look up values from a Dictionary<ContentCategory, Type> that contains types that you manually register when the application starts, or it could find a Type with that name in the loaded assemblies, etc. It’s up to you how that aspect should work.

An example implementation of the DetermineType method that scans all namespaces for types declared in the namespace "MyProject.DeserializableTypes" and below, whose name matches the category name:

public static Type DetermineType(ContentCategory category)
{
    return AppDomain.CurrentDomain.GetAssemblies()
        .SelectMany(a => a.GetTypes())
        .Where(a => a.IsClass && !a.IsAbstract && (a.Namespace + ".").StartsWith("MyProject.DeserializableTypes.", StringComparison.Ordinal))
        .FirstOrDefault(t => t.Name == category.ToString());
}

Personally I would prefer registering types in some way because there’s less room for error that way IMHO.


An alternative approach might be to create a custom attribute that allows you to specify the target type on the enum:

[AttributeUsage(AttributeTargets.Field)]
public class TargetTypeAttribute : System.Attribute
{
    public TargetTypeAttribute(Type targetType)
    {
        TargetType = targetType;
    }

    public Type TargetType { get; }
}

public enum ContentCategory
{
    [TargetType(typeof(ClassOne))]
    ClassOne,
    [TargetType(typeof(ClassTwo))]
    ClassTwo
}

And then you can lazily build a table of enumValue->Type to use as the DetermineType method:

private static readonly Lazy<IReadOnlyDictionary<ContentCategory, Type>> _targetTypes = new Lazy<IReadOnlyDictionary<ContentCategory, Type>>(BuildTypesDictionary);

private static IReadOnlyDictionary<ContentCategory, Type> BuildTypesDictionary()
{
    var dict = new Dictionary<ContentCategory, Type>();
    var enumValues = Enum.GetValues<ContentCategory>();
    foreach (var value in enumValues)
    {
        Type targetType = typeof(ContentCategory).GetField(value.ToString())?.GetCustomAttribute<TargetTypeAttribute>()?.TargetType;
        if (targetType == null)
        {
            throw new Exception($"Enum {nameof(ContentCategory)} doesn't declare a target type for {value}.");
        }
        dict.Add(value, targetType);
    }
    return dict;
}

public static Type DetermineType(ContentCategory category)
{
    if (!_targetTypes.Value.TryGetValue(category, out var type))
    {
        throw new ArgumentException($"Unknown category: {category}", nameof(category));
    }
    return type;
}
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading