TrueWill
TrueWill

Reputation: 25523

Casting to generic type in non-generic method

What I'm doing now:

void Main()
{
    var command1 = new PersistenceCommand(new MyIntBO());
    var command2 = new PersistenceCommand(new MyGuidBO());
    var command3 = new PersistenceCommand(new PersistentBO());

    Console.WriteLine(command1.ToString());
    Console.WriteLine(command2.ToString());
    Console.WriteLine(command3.ToString());
}

public class PersistenceCommand
{
    public PersistenceCommand(PersistentBO businessObject)
    {
        _businessObject = businessObject;
    }

    public override string ToString()
    {
        string result = _businessObject.GetType().Name;

        var keyed = _businessObject as IPrimaryKeyed<int>;

        if (keyed != null)
        {
            result += " " + keyed.Id.ToString();
        }

        return result;
    }

    private readonly PersistentBO _businessObject;
}

public interface IPrimaryKeyed<out TKey>
{
    TKey Id { get; }
}

public class PersistentBO {}

public class MyIntBO : PersistentBO, IPrimaryKeyed<int>
{
    public int Id { get { return 1008; } }
}

public class MyGuidBO : PersistentBO, IPrimaryKeyed<Guid>
{
    public Guid Id
    {
        get
        {
            return new Guid("6135d49b-81bb-43d4-9b74-dd84c2d3cc29");
        }
    }
}

This prints:

MyIntBO 1008
MyGuidBO
PersistentBO

I'd like it to print:

MyIntBO 1008
MyGuidBO 6135d49b-81bb-43d4-9b74-dd84c2d3cc29
PersistentBO

What's the most elegant way to do that?

I want to support all types of keys - int, long, Guid, etc. - so I'd rather not do multiple casts. Note that not every business object implements that interface (some do not have a single primary key).

I realize I could use reflection and try to access the Id property. I was wondering if there's a better solution.

Clarification: To address @Acaz Souza and @Petar Ivanov's answers, we have dozens of classes scattered over multiple assemblies that already implement IPrimaryKeyed<T>. I do not want to break all of them by extending the interface contract. If I were designing this from scratch, their solutions would work.

Upvotes: 0

Views: 307

Answers (3)

Gary.S
Gary.S

Reputation: 7121

Using reflection doesn't seem like a bad way to go here.

ToString method:

// for getting the Id prop
var identProp = _businessObject.GetType().GetProperty("Id");
string result = _businessObject.GetType().Name;

if (identProp != null)
{
    result += " " + identProp.GetValue(_businessObject, null).ToString();
}  

Upvotes: 1

Acaz Souza
Acaz Souza

Reputation: 8631

The problem is in that line:

var keyed = _businessObject as IPrimaryKeyed<int>;

Your other type is not IPrimaryKeyed<int> is IPrimaryKeyed<Guid>, then the if (keyed != null) is false.

You can try do this:

static void Main()
{
    var command1 = new PersistenceCommand(new MyIntBO());
    var command2 = new PersistenceCommand(new MyGuidBO());
    var command3 = new PersistenceCommand(new PersistentBO());

    Console.WriteLine(command1.ToString());
    Console.WriteLine(command2.ToString());
    Console.WriteLine(command3.ToString());
    Console.ReadLine();
}

public class PersistenceCommand
{
    public PersistenceCommand(PersistentBO businessObject)
    {
        _businessObject = businessObject;
    }

    public override string ToString()
    {
        string result = _businessObject.GetType().Name;

        var keyed = _businessObject as IPrimaryKeyed;

        if (keyed != null)
        {
            result += " " + keyed.Id.ToString();
        }

        return result;
    }

    private readonly PersistentBO _businessObject;
}

public interface IPrimaryKeyed
{
    object Id { get; }
}

public class PersistentBO { }

public class MyIntBO : PersistentBO, IPrimaryKeyed
{
    public object Id { get { return 1008; } }
}

public class MyGuidBO : PersistentBO, IPrimaryKeyed
{
    public object Id { get { return new Guid("6135d49b-81bb-43d4-9b74-dd84c2d3cc29"); } }
}

Upvotes: 1

Petar Ivanov
Petar Ivanov

Reputation: 93030

Just create a non-generic interface and replace the generic one with generic abstract class. Then check for the interface:

    public interface IPrimaryKeyed
    {
        object ObjId { get; }
    }

    public abstract class PrimaryKeyed<TKey> : IPrimaryKeyed
    {
        public object ObjId { get { return Id; } }
        public abstract TKey Id { get; }
    }

---

public override string ToString()
{
    string result = _businessObject.GetType().Name;

    var keyed = _businessObject as IPrimaryKeyed;

    if (keyed != null)
    {
        result += " " + keyed.ObjId.ToString();
    }

    return result;
}

Upvotes: 2

Related Questions