kbd
kbd

Reputation: 4449

Generically Casting object to it's 'real' type

I have shared validation logic which largely depends on custom attributes, so I need to check that for each type.

An instance (to check against) is fetched for each validation subscriber:

var instance = this.ValidationProvider.GetInstanceForSubscriber(validationSubscriber);

Where validationSubscriber is an enum with these values:

Person, Checks

At this time, GetInstanceForSubscriber returns an instance of type object:

public object GetInstanceForSubscriber(ValidationSubscribers validationSubscriber)
{
    switch (validationSubscriber)
    {
        case ValidationSubscribers.Person:
            return new Person();
        case ValidationSubscribers.Checks:
            return new Checks();
        default:
            return null;
    }
}

After calling this method, I check the type:

var instanceType = instance.GetType();

And then, I read the (custom) attributes:

var attributes = propertyInfo.GetCustomAttributes(true);

I then return a list of IEnumerable<PropertyAttribute> which contains only my custom attributes, limiting the validation scale to what's necessary only (which is FYI, because not very relevant for this question).

The issue

As instance is of type object, it does not contain any of my custom attributes (logically), so I need to cast it to the correct Class Type.

What's the best way to achieve this?

Upvotes: 1

Views: 126

Answers (2)

Shantanu Gupta
Shantanu Gupta

Reputation: 21198

I would suggest to modify your design by introducing interface.

public interface ICustomAttribute
{
    IEnumerable<PropertyAttribute> GetCustomAttributes(true);
}

class Person:ICustomAttribute
{
    IEnumerable<PropertyAttribute> GetCustomAttributes(true)
    {
         //Validation logic here for Person entity.
         //Encapsulate your entity level logic here.
          return propertyInfo.GetCustomAttributes(true);
    }
}

class Check:ICustomAttribute
{
    IEnumerable<PropertyAttribute> GetCustomAttributes(true)
    {
         //Validation logic here for CHECK entity
         //Encapsulate your entity level logic here.
          return propertyInfo.GetCustomAttributes(true);
    }
}

public ICustomAttribute GetInstanceForSubscriber(ValidationSubscribers validationSubscriber)
{
    //Use dependency injection to eliminate this if possible
    switch (validationSubscriber)
    {
        case ValidationSubscribers.Person:
            return new Person();
        case ValidationSubscribers.Checks:
            return new Checks();
        default:
            return null;
    }
}

Now when you receives an object from this method, you will always have right object.

var instanceType = instance.GetCustomAttributes(true); //will give you all attributes

You can enhance this logic even further by taking advantage of generic types. See this. However it depends how you design your entities, accordingly you can come up with better solution.

Upvotes: 3

Sergey Kalinichenko
Sergey Kalinichenko

Reputation: 726699

One approach is to make overloads for each type that you want to validate:

void ValidateWithAttributes(Person p, Attribute[] attr) {
    ...
}
void ValidateWithAttributes(Checks c, Attribute[] attr) {
    ...
}
// Add an overload to catch unexpected types
void ValidateWithAttributes(Object obj, Attribute[] attr) {
    throw new InvalidOperationException(
        $"Found an object of unexpected type {obj?.GetType()}"
    );
}

Now you can cast your object to dynamic, and let the runtime perform the dispatch:

object instance;
ValidateWithAttributes((dynamic)instance, attributes);

Upvotes: 1

Related Questions