【问题标题】:Invalid cast with generic type泛型类型的无效转换
【发布时间】:2018-01-15 14:54:02
【问题描述】:

我想创建一个函数,该函数接受一个 Position 对象并将该 Position 的纬度和经度作为格式化字符串返回。

但问题是我有 2 个 Position 对象:一个来自 Xamarin Forms,另一个来自地理位置块。

来自 Xamarin 表单的位置:

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

以及来自地理定位块的位置:

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

为了让这两种类型合二为一,我制作了一个界面:

interface IPosition
{
    double Latitude
    {
        get;
        set;
    }

    double Longitude
    {
        get;
        set;
    }
}

所以我的函数应该接受参数中的这两个对象,如下所示:

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

不幸的是,它在运行时抛出错误“System.InvalidCastException: Specified cast is not valid.”。

我做错了什么?我想我需要某种动态演员表,但我不确定。

【问题讨论】:

  • 你怎么打电话给PositionToCoordinates()。考虑在 T 上添加一个约束,例如 PositionToCoordonates&lt;T&gt;(T p) where T : IPosition
  • 我明白创建IPosition背后的原因是如何实现的。我觉得你可能会混淆如何使用它。约束可能是我们的解决方案,但您需要阐明您打算如何使用该方法
  • @LordWilmore 谢谢你的回答。我这样称呼它:GpsDistanceHelper.PositionToCoordonates(currentPosition);所以我应该这样试试? : GpsDistanceHelper.PositionToCoordonates(currentPosition) 其中 T : IPosition; ?
  • @Doctor 首先,该方法没有理由是通用的。你没有得到任何东西是通用的,然后受到限制。只是一开始就不要让它泛化
  • @LordWilmore 这没有任何意义,如果它只能是一种类型而不是参数属于该类型。这里对泛型的需求为零

标签: c# templates interface casting xamarin.forms


【解决方案1】:

PositionToCoordonates 不应该是通用的。使其泛型是一种说法,即参数可以是任何类型。您的参数不能是任何类型,它必须是IPosition。设置IPosition 类型的参数,这样调用者就不能传入另一种类型的对象(因为它们不起作用),并且您将能够从任何编译器错误中看到调用者正在传递什么在错误类型的对象中。

请注意,一旦您更改了参数,将不再有理由使方法成为泛型,您也不需要在方法主体中强制转换参数。

【讨论】:

  • @Doctor 为什么需要将变量转换为该变量的类型?你认为这会实现什么?您如何期望在 IPosition 类型的变量上使用 IPosition 的属性来获得无法转换的异常?
  • @Servy 我认为问题在于 OP 想要访问的两种类型实际上并没有实现 IPosition。 OP 被误解了,认为只创建一个具有相同字段的接口就会自动允许它工作
  • @Doctor 使用 Gabriels 回答。它正是你所需要的
  • @Doctor 然后you 需要将对象转换为实现接口的对象。强制转换只是告诉编译器一个对象实际上是它没有意识到的类型的一种方法,而不是一种将对象变成它不是的东西的方法。
  • @Doctor 不要那样做哈哈
【解决方案2】:

如果您的两个Position 类都实现IPosition,那么您只需要

public static string PositionToCoordonates(IPosition position)

不需要泛型。

另一方面,如果这些类没有实现那个接口(如果你创建了它就是这种情况),你就会有一些代码重复。

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(...);
}

作为对我回答的第二部分的澄清:在 C# 中,类必须声明它们实现了一个接口,在该接口上定义属性/方法是不够的。

因此,在您的示例中,position as IPosition 将是 null,而(IPosition) position 将引发异常。

为了使上述转换起作用,Position 必须声明为

class Position : IPosition
{
    ...
}

【讨论】:

  • 感谢您的回答。这两个位置对象不是我的。所以我不能让他们实现我的 IPosition 接口。现在我要避免的是您建议的代码重复。但也许没有其他办法;-)
  • 作为替代方案,您可以查看dynamic,但这对于这种特殊情况来说太过分了。
  • @Doctor 没有代码重复。所有的“特殊酱汁”都在PositionToCoordonates(double latitude, double longitude) 方法中。另外两个只是简单的重载,它们提取所需的实际值并调用“特殊酱汁”
  • @maccettura 好的,那我会尽快尝试。谢谢大家!
  • 我注意到一个Positionstruct,另一个是class... 支持@GabrielNegut 方法的另一个理由。
【解决方案3】:

因为Positions 都不是由您实现的,所以您不能只发明一个接口来将其用于强制转换恕我直言。在这种情况下,我可能会按如下方式处理:

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; }
}

对于这种只涉及 2 种可能类型的场景,动态似乎太重了。我不知道是否确实需要泛型参数,但我把它留给了希望,一次可以避免装箱/拆箱。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-11-28
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-26
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多