MyDaftQuestions
MyDaftQuestions

Reputation: 4701

Unable to cast 2 objects although they share same interface

I would like to map 2 objects but I don't want to rely on 3rd party tools - the reason is to help me get a better understanding of what those 3rd party tools may do.

I have an interface, which has string Foo and bool Bar

public interface ITest
{
    string Foo { get; set; }
    bool Bar { get; set; }
}

My first class, called A implements ITest

public class A : ITest
{
    public string Foo { get; set; }
    public bool Bar { get; set; }
}

And finally, Class B also implements ITest but has an additional property

public class B : ITest
{
    public string Foo { get; set; }
    public bool Bar { get; set; }
    public int Other { get; set; }
}

Since both classes share a common interface, how can I cast one to the other?

var a = new A();
var b = new B();
a= b as A; //does not work but this would be ideal

I don't want to Map by explicitly stating the names of the properties.

As such, having (where the B constructor maps) is also not ideal

var a = new A();
var b = new B(a); //not desired

Is this possible? It would mean that only the properties that were defined in the interface would be copied across.

Answers like C# map two complex objects are not ideal as I have to explicitly state the properties.

Upvotes: 0

Views: 929

Answers (6)

Vera
Vera

Reputation: 734

You can use reflection to automatically detect properties and map their values.

private static T GetMappedObject<T>(object source)
{
    var output = Activator.CreateInstance(typeof(T));
    var sourceType = source.GetType();

    foreach (var outputProperty in typeof(T).GetProperties())
    {
        if (sourceType.GetProperty(outputProperty.Name) is PropertyInfo sourceProperty)
        {
            var value = sourceProperty.GetValue(source);
            outputProperty.SetValue(output, value);
        }
    }

    return (T)output;
}

You can then call this method like var b = GetMappedObject<B>(a);.

Note that the above snippet does not check whether the value on the source object can actually be mapped to the output property, and it does require objects of type T to define a parameterless constructor.

As someone pointed out in the comments, if not for the educational nature of your question, it is still recommended to use a proven 3rd party tool like AutoMapper where you can for production code.

Upvotes: 2

ChrisBD
ChrisBD

Reputation: 9209

Both classes implement ITest interface BUT class B also has an extra property which prevents you from casting between the A and B.

You can however have functions that handle ITest, but treat them differently e.g.

public void SaveDataFunction(ITest param)
{
    if (param is A)
    {
        SaveDataTypeA((A)param);
    }
    else if (param is B)
    {
        SaveDataTypeB((B)param);
    }
}

private void SaveDataTypeA(A value)
{
}

private void SaveDataTypeB(B value)
{
}

Upvotes: 0

Christopher
Christopher

Reputation: 9824

There is no implicit conversion between A and B. You can fit both of them in variables of type ITest, but that is where the automatics end. C#/.NET is very conserviative with implicit conversions.

And if your example would work, it would also have to work between System.String and System.Windows.Forms.Form as they both inherit from Object. Every Class in .NET implicitly or explicitly inherits from Object, so that would be a nightmare.

What you need to do is either:

  • give both classes a proper function for the conversion. Usually a constructor taking a instance of the other type. You could use ITest as the type for those functions. That would keep repitition to a minimum. A(ITest initializeFrom)
  • write a explicit conversion between those two types: https://learn.microsoft.com/en-us/dotnet/csharp/programming-guide/types/casting-and-type-conversions
  • Actually solve it down to ITest having everything you need, so putting all thoe instances into a ITest variable would give you all you need

Upvotes: 2

Salah Akbari
Salah Akbari

Reputation: 39966

You can use explicit operator, so something like this maybe what you are looking for:

public class A : ITest
{
    public string Foo { get; set; }
    public bool Bar { get; set; }

    public static explicit operator A(B v)
    {
        return new A { Bar = v.Bar, Foo = v.Foo };
    }
}

And then use casting instead:

a = (A)b;

Upvotes: 0

John Lord
John Lord

Reputation: 2185

You can use json for this. We do this all the time to cast DataTable into a model.

You Serialize the first model and Deserialize it into the other one. Doing it this way, only the fields that are in common are touched.

        var deserializeSettings = new JsonSerializerSettings { ObjectCreationHandling = ObjectCreationHandling.Replace };
        string serializedObject = JsonConvert.SerializeObject(A, deserializeSettings);
        B b = JsonConvert.DeserializeObject<B>(serializedObject);

Upvotes: 0

Radu Diță
Radu Diță

Reputation: 14191

You have at least 2 options.

Either cast to the interface ITest: b as ITest

Make B extend A. This way casting b to A is safe.

Upvotes: 0

Related Questions