简体   繁体   中英

Invalid cast with generic type

I want to make a function that takes a Position object and returns the latitude and longitude of this Position as a formated string.

But the problem is that I have 2 Position objects : one from Xamarin Forms and another one from a geolocation nugget.

Position from Xamarin forms:

public Position(double latitude, double longitude);
public double Latitude { get; }
public double Longitude { get; }
...

And Position from the geolocation nugget:

public Position();
public Position(Position position);
public double Latitude { get; set; }
public double Longitude { get; set; }
...

In order to have the two types working as one I made an interface :

interface IPosition
{
    double Latitude
    {
        get;
        set;
    }

    double Longitude
    {
        get;
        set;
    }
}

So my function should accept those two objects in parameter as follow :

public static string PositionToCoordonates<T>(T p)
    {
        return ((IPosition)p).Latitude.ToString().Replace(",", ".") + " ," + ((IPosition)p).Longitude.ToString().Replace(",", ".");
    }

Unfortunatly it throws the error " System.InvalidCastException: Specified cast is not valid. " at runtime.

What am I doing wrong ? I guess that I need some kind of dynamic cast but I'm not sure.

PositionToCoordonates shouldn't be generic. Making it generic is a way of saying that the parameter can be of any type . Your parameter can't be of any type, it needs to be an IPosition . Make the parameter of type IPosition , so that callers aren't allowed to pass in objects of another type (as they won't work), and you'll be able to see, from any compiler errors, what callers are passing in an object of the wrong type.

Note that once you change the parameter there will no longer be a reason for the method to be generic, nor will you need to be casting the parameter in the method body.

If both your Position classes implement IPosition , all you need is

public static string PositionToCoordonates(IPosition position)

There is no need for generics.

On the other hand, if those classes do not implement that interface (and that would be the case if you created it), you will have a bit of code duplication.

public static string PositionToCoordonates(Geolocation.Position position)
{
    return PositionToCoordinates(position.Latitude, position.Longitude);
}

public static string PositionToCoordonates(Xamarin.Position position)
{
    return PositionToCoordinates(position.Latitude, position.Longitude);
}

private static string PositionToCoordonates(double latitude, double longitude)
{
    return string.Format(...);
}

As a clarification on the second part of my answer: In C#, classes have to declare they implement an interface, it is not enough to have the properties/methods defined on that interface.

So, in your example, position as IPosition will be null , and (IPosition) position will throw an exception.

For the above casts to work, Position has to be declared as

class Position : IPosition
{
    ...
}

Because both Positions are not implemented by you you can't just invent an interface to use it for casts imho. In this scenario I would probably handle this as follows:

void Main()
{
    var position1 = new Position1() { Lat = 10, Lon = 5 };
    var position2 = new Position1() { Lat = 12, Lon = 3 };
    Console.WriteLine(GetLatLon(position1));
    Console.WriteLine(GetLatLon(position2));
}

static string GetLatLon<T>(T input) 
{
    var position1 = input as Position1;
    var position2 = input as Position2;
    if (position1 != null)
    {
        return $"{position1.Lat}-{position1.Lon}";
    }
    if (position2 != null)
    {
        return $"{position2.Lat}-{position2.Lon}";
    }
    throw new ArgumentException(nameof(input));
}

class Position1 
{
    public double Foo { get; set ;}
    public int Lat { get; set;}
    public int Lon { get; set;}
}

class Position2 
{
    public int Lat { get; set; }
    public int Lon { get; set; }
}

Dynamic seems to be too heavy for this scenario where only 2 possible types are involved. I don't know if the generic parameter is needed exactly but I left it in the hope, that boxing/unboxing will be avoided one time.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM