Skip to content

VistaDB .Net Database Blog
Syndicate content
Unlimited Possibilities through Code
Updated: 7 hours 19 min ago

Using a custom attribute to determine type at runtime

Mon, 02/06/2012 - 17:46

On a Windows Phone 7 project I am currently building I had a need to build a factory that instantiates different concrete classes depending upon an object at runtime.   I have a number of information feeds, that all have different classes built to actually go get their data.  One for RSS Feeds, one for Twitter, etc.  Each Feed has a property for the FeedType, this is the information needed to determine what class to instantiate at runtime.

I have written this pattern many times, but I ran across an interesting post on StackOverflow where Steven gave a different approach to solving this without the classic giant switch statement in the Create method.   I thought it was neat enough to use in my application and share here.

There are a couple of steps you have to take, but none of them are difficult.

  • Decorate your objects with a custom attribute
  • Build the object types using reflection
  • Implement your factory method
Custom Attribute

The attribute I added is called FeedTypeAttribute, I then add it to the concrete feed processor classes.

class FeedTypeAttribute : Attribute
{
private int _feedType;

public FeedTypeAttribute(int feedType)
{
_feedType = feedType;
}

public int FeedTypeId
{
get
{
return _feedType;
}
}
}

Adding the attribute to a class looks like the following.  
[FeedTypeAttribute(1)]
public class StaticFeedProcessor : IFeedProcessor
{
public StaticFeedProcessor()
{

}

public void ProcessFeedModel(FeedModel feedModel)
{
throw new NotImplementedException();
}
}

Note that this class would be a FeedType of 1 in my data, but how do you determine what class that is during runtime?  The answer is reflection (and yes, this does work on Windows Phone 7).   Factory Method Using Custom Attributes

public class FeedProcessorFactory : IFeedProcessorFactory
{
private static Dictionary<int, Type> processorList = new Dictionary<int, Type>();

static FeedProcessorFactory()
{
// Get the types in this assembly that implement our custom attribute
var targetTypes = from type in Assembly.GetExecutingAssembly().GetTypes()
where type.CanBeTreatedAsType(typeof(IFeedProcessor))
where !type.IsAbstract && !type.IsInterface
let customAttributes = type.GetCustomAttributes( typeof(FeedTypeAttribute), false)
let attribute = customAttributes[0] as FeedTypeAttribute
select new { type, attribute.FeedTypeId };

processorList = targetTypes.ToDictionary(p => p.FeedTypeId, p => p.type);
}

public IFeedProcessor CreateProcessorByFeedType(int feedId)
{
Type feedType = processorList[feedId];

return Activator.CreateInstance(feedType) as IFeedProcessor;
}
}

The key here is the type.GetCustomAttributes() call.  The linq expression gets all the type of the current assembly where the type can be assigned from the IFeedProcessor class.  We have to ensure the class is not abstract or an interface, and then get the custom attributes. 

The let clause in the linq expression can be thought of like a local temporary variable.  Assigning the attributes to it, and then processing them in the final select.

Consuming the Factory

Consuming the factory is then as simple as instantiating a factory object and calling the CreateProcessorByFeedType function.  A fully allocated processor is returned ready to handle the feed.

// This is in my container object
private static IFeedProcessorFactory _feedProcessorFactory = new FeedProcessorFactory();


// This is in the function to process the feeds
IFeedProcessor processor = _feedProcessorFactory.CreateProcessorByFeedType(feed.FeedType);
processor.ProcessFeedModel(feed);
Less Coupling is Good This code allows for the addition of new processor classes just by building the class and adding the custom attribute.  Of course you should check if the feedtype is not supported before trying to use the object (I omitted some code used for safety checking).   If a newer class is created in the future to replace a current one I only have to change the custom attribute to handle it.  No switch statements, or remembering to update a config somewhere.
Categories: Companies

Is this .Net Type Assignable from another Type?

Fri, 02/03/2012 - 01:20

I have run into this a few times and always have to go to the MSDN to find the answer.  I have a situation where I needed to determine if a given type can be treated as a base type in an object factory.  I wanted to be able to create my concrete classes without having to do a switch statement for each type.

First I had to determine if the types were assignable to each other, then find the type to implement.  I will cover that in another article.  In this one I just want to cover the assignment test.

Can this type be assigned?

There is a function IsAssignableFrom that you can call on a System.Type. 

if( ParentType is ChildType )

It actually has to be written the other way around.

if( ChildType.IsAssignableFrom(ParentType) )

Quick Extension Method

So instead of having to do that all over my code, I wrote an extension method to allow me to do it following a different ordering.  I actually got this basic idea from one of the comments on the MSDN page.

if( parentType.CanBeTreatedAsType(childType) )

As you can see the extension method lets you write the syntax in a more natural manner.  Your preference may differ, but this makes more sense to me.

Example Code
public static class DataExtensionMethods
{
public static bool CanBeTreatedAsType(this Type CurrentType, Type TypeToCompareWith)
{
if (CurrentType == null || TypeToCompareWith == null)
return false;

return TypeToCompareWith.IsAssignableFrom(CurrentType);
}
}

void Main()
{
System.Type parentType = typeof(ParentClass);
System.Type childType = typeof(ChildClass);

bool ChildToParent = childType.CanBeTreatedAsType(parentType);
Console.WriteLine("Child can be treated as parent: " + ChildToParent );

bool ParentAsChild = parentType.CanBeTreatedAsType(childType);
Console.WriteLine("Parent can be treated as child: " + ParentAsChild );
}

public class ParentClass
{
public ParentClass()
{
}
}

public class ChildClass : ParentClass
{
public ChildClass() : base()
{

}
}

Categories: Companies

Debug Secondary Tiles

Wed, 01/04/2012 - 18:39

This is another of those posts for my future self, because I know I won’t remember this little tip.

Set startup through app manifest

The WMAppManifest.xml has a property that tells it where to send the default launch of the application.

    <Tasks>
      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
    </Tasks>

So the normal launch page is MainPage.xaml. But you can change it to another page, and include your parameters just like from a secondary tile!

    <Tasks>
      <DefaultTask Name="_default" NavigationPage="/TileDetails.xaml?myid=2" />
    </Tasks>

Now you can just press F5 and debug just as if that secondary tile had been clicked.

Categories: Companies

Coding 4 Fun Phone Toolkit updated

Tue, 01/03/2012 - 18:37

If you have not looked at the toolkit before you seriously owe it to yourself, go get it now!

Coding 4 Fun Phone Toolkit

Windows Phone Geek has also done a great intro post on one of the new controls (the MetroFlow control).  Getting started with MetroFlow Control is a great read, even just to get up to speed with the overall concepts of the toolkit.

Categories: Companies

Debug Windows Phone 7 Secondary Tiles

Sun, 12/18/2011 - 00:00

This is another of those posts for my future self, because I know I won’t remember this little tip.

Set startup through app manifest

The WMAppManifest.xml has a property that tells it where to send the default launch of the application.

    <Tasks>
      <DefaultTask Name="_default" NavigationPage="MainPage.xaml" />
    </Tasks>

So the normal launch page is MainPage.xaml.  But you can change it to another page, and include your parameters just like from a secondary tile!

    <Tasks>
      <DefaultTask Name="_default" NavigationPage="/TileDetails.xaml?myid=2" />
    </Tasks>

Now you can just press F5 and debug just as if that secondary tile had been clicked.

Categories: Companies