0

I have this situation

public class Base
{
    public Basedef def;
}

public class A : Base
{
}

public class B : A
{
    public int GetBar()
    {
        return def.bar;
    }
}


public class BaseDef
{
}

public class ADef : BaseDef
{
    public int foo;
}

public class BDef : ADef
{
    public int bar;
}

As you an see, there is an error in method B:GetBar(), because def have no access to bar, but if you make...

public int GetBar()
{
    return (def as BDef).bar;
}

should work, but i want to avoid casting, how to get properties from definition using reference created in Base class without using casting?

Why avoid cast?, because is prone to runtime errors and is easier to introduce bugs, I want type-safe coding.

What I am trying to do

public class Factory
{
    public static Base<BaseDef> Create(BaseDef d)
    {
        if(d is BDef)
            return new B(); //Error, can not convert B to Base<BaseDef>
    }
}

public class Program
{
    B instance = Factory.Create(new BDef()); //Error, can not convert to Base<BaseDef> to B
}

I am looking for an elegant solution

bye!

Joe Cabezas
  • 1,397
  • 3
  • 15
  • 21

4 Answers4

2

To have an elegant, no-casting solution, the compiler needs to know that def is a BDef in GetBar(). This is a way to do that, that I think will work well for your situation:

public class Base<T> where T : BaseDef
{
    public T def { get; set; }
}

public class A<T> : Base<T> where T : ADef
{
    public int GetFoo()
    {
        return def.foo; // this works, too
    }
}

public class B : A<BDef>
{
    public int GetBar()
    {
        return def.bar;
    }
}

(By the way, you should use public properties, not public fields. See Honestly, what's the difference between public variable and public property accessor? for some reasons why.)

Update: your Factory method might look like one of these:

public static Base<T> Create<T>(T d) where T : BaseDef
{
    if(typeof(T) == typeof(BDef))
        return (Base<T>)(object)new B();
    else
        return null;
}
public static T Create<T, U>(U d) where T : Base<U> where U : BaseDef
{
    T result;
    if (typeof(T) == typeof(B))
        result = (T)(object)new B();
    else
        throw new NotImplementedException();
    result.def = d;
    return result;
}
public static T CreateAlternate<T, U>(U d) where T : Base<U>, new() where U : BaseDef
{
    return new T { def = d };
}

Used like:

void Main()
{
    Factory.Create(new BDef());
    Factory.Create<B, BDef>(new BDef());
    Factory.CreateAlternate<B, BDef>(new BDef());
}

I like the last one because there's no casting, as long as the new() constraint isn't an issue, or the first if concise calling code is very valuable (because the generic type can be inferred).

Community
  • 1
  • 1
Tim S.
  • 55,448
  • 7
  • 96
  • 122
  • I thought about this, it is possible for A to get foo value using its def reference? – Joe Cabezas Sep 13 '13 at 19:09
  • @JoeCabezas Yes, my solution supports this, because the compiler knows that `def` is an `ADef`. – Tim S. Sep 13 '13 at 19:11
  • this is giving me errors: Base b = new A(); (Cannot implicitly convert type `A' to `Base') – Joe Cabezas Sep 13 '13 at 19:20
  • For the same reason that `Collection c = new ObservableCollection();` is invalid: It simply doesn't make sense. How are you trying to use this? You might benefit from a `Base`, which would be a base class of `Base`. If you only needed `T` to be `in` or `out`, you can use [co/contravariance](http://msdn.microsoft.com/en-us/library/dd233059.aspx) on a generic interface, but as-is you have a `get` and a `set`, so that won't work. – Tim S. Sep 13 '13 at 19:24
  • added in my question "what i am trying to do", check it out if you have time please – Joe Cabezas Sep 13 '13 at 19:43
  • @JoeCabezas I've added some example `Factory` methods for you. – Tim S. Sep 13 '13 at 19:55
1

It's not type safe to do it with the cast because what you're trying to do is fundamentally not type safe.

You have a class A and a class B that are subclasses of Base, and Base has a reference to BaseDef. BaseDef could be ADef or BDef, you don't know which one, certainly not from anything that is told to B.

But you can provide B with the information it needs to know that its BaseDef reference is actually a BDef, if you use generics.

public class Base<T> where T : BaseDef
{
    public T def;
}

public class A<T> : Base<T> where T : ADef
{
}

public class B : A<BDef>
{
    public int GetBar()
    {
        return def.bar;
    }
}
moron4hire
  • 703
  • 5
  • 13
1

Why avoid cast?, because is prone to runtime errors and is easier to introduce bugs, I want type-safe coding.

I don't understand why cast is error prone, If you don't know what will be the RuntimeType of the def I'll say your design is wrong.

In my opinion you should be knowing what will be the runtime type of it.

There are workarounds

Workaround 1:

public int GetBar()
{
    if (def is BDef)
        return ((BDef)def).bar;

    return 0;//some default value
}

Workaround 2: Introduce a enum saying who am I.

public enum DefType
{
    BaseDef = 0,
    ADef =1,
    BDef =2
}

public class BaseDef
{
    public virtual DefType MyType
    {   
        get{ return  DefType.BaseDef; }
    }
}

public class ADef
{
    public override DefType MyType
    {   
        get{ return  DefType.ADef; }
    }
}

Then use it like

switch(def.MyType)
{
    case DefType.ADef:
    {
        (ADef).foo;//you know who is this here right?
    }
    ...
}
Sriram Sakthivel
  • 72,067
  • 7
  • 111
  • 189
0

Does it make sense to make Basedef an abstact class and bar an abstract property on it?

ps.
  • 4,230
  • 5
  • 31
  • 39