views:

69

answers:

5

Do special processing for a type in a generic class Pinnew member dan neely 19mins ago

I'm trying to roll up some old (originally .net 1.1) abstract classes into generics. The classes in question all provide similar functionality for a data object of a specific type. For the most part things are going well, but I've ran into a few places where one one of the data objects is of a type that needs extra processing in one method beyond what all the other types need. I can check the type of T to see if it's the type I need to do the special processing for, but the cast from T to SpecialType won't compile. Is there a different way I can do this, or is what I want to do impossible?

class MyGenericClass : ICloneable where T: class, new()
{

private T  m_storedClass;
...

private DoStuff()
{
   //do stuff for all types

   //objects of SpecialType need extra stuff done.
   if (typeof(T) == typeof(SpecialType))
   {
      //compiler error:  Error Cannot convert type 'T' to 'SpecialType'
      ((SpecialType)m_storedClass).SpecialString = "foo";
   }
}
A: 

This won't work unless you do this

class MyGenericClass : ICloneable 
    where T: class, new(), SpecialType

or this

class MyGenericClass : ICloneable 
    where T: class, new(), BaseTypeOfSpecialType

or maybe this:

class MyGenericClass : ICloneable 
    where T: class, new(), InterfaceOnSpecialType

Perhaps you don't need to do this though - if you post a more complete problem maybe I can help you figure out a way to avoid having to do this at all.

Andrew Hare
+2  A: 

How about something like this:

interface ISomething
{
 void DoSomething();
}

class NormalType : ISomething
{
 // ...
 public void DoSomething() { /* nothing to do */ }
}

class SpecialType : ISomething
{
 // ...
 public void DoSomething() { this.SpecialString = "foo" }
}

class MyGenericClass : ICloneable
{
 private ISomething m_storedClass;

 private DoStuff()
 {
  // ...
  m_storedClass.DoSomething();
 }
}
IRBMe
+1 - this would be a much better way to do it.
Eric Petroelje
+1 - Much better. Another way to go, if youi can't modify the stored class, is to specialize on T and TSomething.
Steven Sudit
At the point of having to maintain separate classes implementing the interface, am I really gaining anything over the legacy abstract/inherited classes approach?
Dan Neely
Please ignore my previous comment. I misread your example as suggesting I have the interface as part of MYGenericClass, not the objects it holds.
Dan Neely
using an interface instead of a class in MyGenericClass's constraints costs me the ability to have T be nullable. Is there any way to readd the constraint?class MyGenericClass : ICloneable where T: IMyInterface, new()
Dan Neely
A: 

You could make it work by using constraints, but this code has a funny smell to it.

Is there another way you could get that extra processing in there without breaking the "genericness" of your class?

Eric Petroelje
Agreed. There's a bit of a code smell here but without knowing more about the exact code, it's hard to tell what a good refactoring would be.
IRBMe
The smell several of you've noticed is having to output data for a 3rd party app whose file format doesn't allow for storing all of the data that my app needs. In this case SpecialType needs to track if the 3rd party file has been created or not. I'm setting the value based on the existence of the second file instead of storing it as a flag in my file to avoid the risk of the state getting corrupt (eg someone deletes the 3rd party file via explorer.)
Dan Neely
To clarify a bit more. MyGenericClass also manages other data file formats that don't need any 3rd party hoop jumping.
Dan Neely
A: 

It doesn't work in C#, the compiler won't let you do that... If you can't live with the interface/base class constraints that others have suggested, I have a not-so-nice but working solution:

The cast you're trying to do works in C++/CLI. You have to decide for yourself if it's worth it.

chris166
Even without Daniel Brückner's working C# options C++/CLI would've been massive overkill.
Dan Neely
Agreed, I didn't think it's possible in C#, but it's nice to learn something new
chris166
+2  A: 

The following compiles fine.

if (typeof(T) == typeof(SpecialClass))
{
    (this.m_storedClass as SpecialClass).SpecialOperation();
}

Casting to Object first is another solution.

if (typeof(T) == typeof(SpecialClass))
{
    ((SpecialClass)((Object)this.m_storedClass)).SpecialOperation();
}

Just to note, the check could also be rewritten to the following.

if (this.m_storedClass is SpecialClass)
{
    (this.m_storedClass as SpecialClass).SpecialOperation();
}

Or using only a single as operator including a non-null check in the condition for free.

SpecialClass special = this.m_storedClass as SpecialClass;
if (special != null)
{
    special.SpecialOperation();
}
Daniel Brückner