Sunday, April 17, 2011

c# - cast generic class to its base non-generic class

I have following classes:

public abstract class CustomerBase
{
 public long CustomerNumber { get; set; }
 public string Name { get; set; }
}

public abstract class CustomerWithChildern<T> : CustomerBase
 where T: CustomerBase
{
 public IList<T> Childern { get; private set; }

 public CustomerWithChildern()
 {
  Childern = new List<T>();
 }
}

public class SalesOffice : CustomerWithChildern<NationalNegotiation>
{
}

The SalesOffice is just one of few classes which represent different levels of customer hierarchy. Now I need to walk through this hierarchy from some point (CustomerBase). I can't figure out how to implement without using reflection. I'd like to implement something like:

 public void WalkHierarchy(CustomerBase start)
 {
  Print(start.CustomerNumber);
  if (start is CustomerWithChildern<>)
  {
   foreach(ch in start.Childern)
   {
    WalkHierarchy(ch);
   }
  }
 }

Is there any chance I could get something like this working?


The solution based on suggested has-childern interface I implemented:

public interface ICustomerWithChildern
{
 IEnumerable ChildernEnum { get; }
}

public abstract class CustomerWithChildern<T> : CustomerBase, ICustomerWithChildern
 where T: CustomerBase
{
 public IEnumerable ChildernEnum { get { return Childern; } }

 public IList<T> Childern { get; private set; }

 public CustomerWithChildern()
 {
  Childern = new List<T>();
 }
}

 public void WalkHierarchy(CustomerBase start)
 {
  var x = start.CustomerNumber;
  var c = start as ICustomerWithChildern;
  if (c != null)
  {
   foreach(var ch in c.ChildernEnum)
   {
    WalkHierarchy((CustomerBase)ch);
   }
  }
 }
From stackoverflow
  • "Is" and "As" only work on fully qualified generic types.

    See this MSDN discussion for details including workarounds.

    The most common workaround I've seen is to add an interface to the mix that your CustomerWithChildren could implement, and check for that interface.

  • You could move the WalkHierarchy method to the base class and make it virtual. The base class implementation would only process the current node. For the CustomerWithChildern<T> class, the override would do an actual walk.

    recursive : This is the best solution as it encapsulates the walking implementation and just does the right thing without forcing the caller to be aware of the implementation details.
    John Saunders : High praise from someone named, "recursive". ;-)
    OregonGhost : My last large .NET project had a similar requirement, and John Saunders' way is much like I did it. Made many things so much easier :)
  • Explicitly with that method, no. However you can achieve the same functionality with an interface. In fact, you could just have your generic class implement IEnumerable. It's also worth noting that your class should also have "where T : CustomerBase" in order to ensure type safety.

  • I believe that you want to make the lookup for the determination of doing to the walk an interface.

    So maybe add an "IWalkable" interface that exposes the information needed to do the walk, then you can create your method checking to see if the passed object implements the interface.

  • I think everyone hits this "issue" when first working with generic classes.

    Your first problem is hinted at in your question phrasing: an open generic type is NOT the base class to a closed one. There is no OO relationship here, at all. The real base class is CustomerBase. An "open" generic type is like a half-completed class; specifying type arguments "closes" it, making it complete.

    While you can do:

    Type t = typeof(CustomerWithChildern<>)
    

    the condition

    typeof(CustomerWithChildern<>).IsAssignableFrom(CustomerWithChildern<Foo>)
    

    will always be False.

    -Oisin

  • Try this:

    if(start.GetType().GetGenericTypeDefinition() == typeof(CustomerWithChildern<>))
    

0 comments:

Post a Comment