Haffi112
Haffi112

Reputation: 523

C# getting details from subclasses

I'm a complete newbie to C# so excuse me if this looks weird.

I have an abstract class called vefHlutir

namespace Klasasafn
{
    public abstract class vefHlutur
    {
        public abstract List<String> columnNames();
        public abstract List<String> toStringList();
    }
}

//Here is an object that inherits from this abstract class:

namespace Klasasafn
{
    [Table(Name="Users")]
    public class User: vefHlutur
    {
        public override List<String> columnNames()
        {
            List<String> p = new List<String>();
            p.Add("Nafn");
            p.Add("Email");
            p.Add("Lýsing");
            return p;
        }
        public override List<String> toStringList()
        {
            List<String> p = new List<String>();
            p.Add(name);
            p.Add(email);
            p.Add(descr);
            return p;
        }
    ... more stuff here
    }

} 

//And here is the code I'm trying to run, Item, User and Category all inherit from vefHlutir:

List<Klasasafn.Item> hlutir;
List<Klasasafn.User> notendur;
List<Klasasafn.Category> flokkar;
void Page_Init(object sender, EventArgs e)
{
    hlutir = Fac.getItemList();
    notendur = Fac.getUserList();
    flokkar = Fac.getCategoryList();

    prenta(notendur, Table1);
}

protected void Page_Load(object sender, EventArgs e)
{

}
protected void DropDownList1_SelectedIndexChanged(object sender, EventArgs e)
{

}
protected void Button1_Click(object sender, EventArgs e)
{
    if (DropDownList1.SelectedIndex == 0)
    {
        prenta(notendur, Table1);
    }
    else if (DropDownList1.SelectedIndex == 1)
    {
        prenta(hlutir, Table1);
    }
    else
        prenta(flokkar, Table1);
}
private void prenta(List<vefHlutur> foo, Table f)
{
    List<String> columnNames = foo[0].columnNames();
    TableRow tRow1 = new TableRow();
    f.Rows.Add(tRow1);
    foreach (String i in columnNames)
    {
        TableCell columnNameCell = new TableCell();
        tRow1.Cells.Add(columnNameCell);
        columnNameCell.Controls.Add(new LiteralControl(i));
    }
    foreach (vefHlutur j in foo)
    {
        TableRow tRow = new TableRow();
        f.Rows.Add(tRow);
        List<String> töfluHlutir = j.toStringList();
        foreach (String k in töfluHlutir)
        {
            TableCell tCell1 = new TableCell();
            tRow.Cells.Add(tCell1);
            tCell1.Controls.Add(new LiteralControl(k));
        }
    }
}

My problem is that I can't use the method prenta.

I always get these errors:

Error 1 The best overloaded method match for 'Forsíða.prenta(System.Collections.Generic.List, System.Web.UI.WebControls.Table)' has some invalid arguments

Error 2 Argument '1': cannot convert from 'System.Collections.Generic.List' to 'System.Collections.Generic.List

How do I solve this?

Upvotes: 2

Views: 1195

Answers (2)

johnnycrash
johnnycrash

Reputation: 5344

There is a way to do the cast. A little unsafe code! Don't be afraid of this post. Its mostly test code to show that it works. All the work happens here:

static unsafe List<A> CastBasAIL(List<B> bIn) {

  DynamicMethod dynamicMethod = new DynamicMethod("foo1", typeof(List<A>), 
   new[] { typeof(List<B>) }, typeof(void));
  ILGenerator il = dynamicMethod.GetILGenerator();
  il.Emit(OpCodes.Ldarg_0);                     // copy first argument  to stack
  il.Emit(OpCodes.Ret);                         // return the item on the stack
  CCastDelegate HopeThisWorks = (CCastDelegate)dynamicMethod.CreateDelegate(
   typeof(CCastDelegate));

  return HopeThisWorks(bIn);

}

This solution works as long as the thing you are trying to cast has the same instance field layout as the thing you are casting it to (inheritance situations work well). Note, there are some things that will give you type mismatch errors: i.e. if the List attempts to create a base type in a covariant situation. Just test after doing this.

I apologize to purists for this, but I am a recovering c/c++vb/aseembly programmer!

namespace Covariant {

  class A {
    public virtual string Name() { return "A"; }
  }

  class B : A {
    public override string Name() { return "B"; }
  }

  delegate List<A> CCastDelegate(List<B> b);  // be used in the cast

  class Program {

    static unsafe List<A> CastBasAIL(List<B> bIn) {

      DynamicMethod dynamicMethod = new DynamicMethod("foo1", typeof(List<A>), new[] { typeof(List<B>) }, typeof(void));
      ILGenerator il = dynamicMethod.GetILGenerator();
      il.Emit(OpCodes.Ldarg_0);                     // copy first argument  to stack
      il.Emit(OpCodes.Ret);                         // return the item on the stack
      CCastDelegate HopeThisWorks = (CCastDelegate)dynamicMethod.CreateDelegate(typeof(CCastDelegate));

      return HopeThisWorks(bIn);

    }

    static void Main(string[] args) {

      // make a list<B>
      List<B> b = new List<B>();
      b.Add(new B());
      b.Add(new B());

      // set list<A> = the list b using the covariant work around
      List<A> a = CastBasAIL(b);

      // at this point the debugger is miffed with a, but code exectuing methods of a work just fine.
      // It may be that the debugger simply checks that type of the generic argument matches the 
      // signature of the type, or it may be that something is really screwed up.  Nothing ever crashes.

      // prove the cast really worked
      TestA(a);

      return;

    }

    static void TestA(List<A> a) {

      Console.WriteLine("Input type: {0}", typeof(List<A>).ToString());
      Console.WriteLine("Passed in type: {0}\n", a.GetType().ToString());

      // Prove that A is B
      Console.WriteLine("Count = {0}", a.Count);
      Console.WriteLine("Item.Name = {0}", a[0].Name());

      // see if more complicated methods of List<A> still work
      int i = a.FindIndex(delegate(A item) { return item.Name() == "A"; });
      Console.WriteLine("Index of first A in List<A> = {0}", i);
      i = a.FindIndex(delegate(A item) { return item.Name() == "B"; });
      Console.WriteLine("Index of first B in List<A> = {0}\n", i);

      // can we convert a to an array still?
      Console.WriteLine("Iterate through a, after converting a to an array");
      foreach (var x in a.ToArray())
        Console.WriteLine("{0}", x.Name());

    }
  }
}

Upvotes: 0

JaredPar
JaredPar

Reputation: 754665

The problem is that in C#, the type List<ChildClass> cannot be used when the method is typed for List<ParentClass>. This type of conversion is known as covariance and it will not be available in C# until 4.0 and then only on interfaces and events.

What you can do though is make the method generic and add a constraint.

private void prenta<T>(List<T> foo, Table f)
  where T : vefHlutur
{
  ...
}

What this code is doing is saying that prenta will accept a List<T> as the first parameter for any case where T is or derivecs from the type vefHlutur. It also allows you to treat the type T as if it is the type vefHlutur with respect to calling methods, properties, etc ... This should allow your scenario to work.

Upvotes: 9

Related Questions