首页 > 解决方案 > 当从 C# 应用程序调用时,为什么在 f# 中实现的类型与 C# 类型的行为不同?

问题描述

我正在将我的 C# 代码移植到 F# 库中。我的 C# 库中有以下接口/类:

public interface IMatch<out T> where T : IGame
{
    IEnumerable<T> Games { get; }
}

public interface IGame
{
    string Name { get; }
}

public class SoccerMatch : IMatch<SoccerGame>
{
    public SoccerMatch(IEnumerable<SoccerGame> games)
    {
        Games = games;
    }

    public IEnumerable<SoccerGame> Games { get; }
}

public class SoccerGame : IGame
{
    public SoccerGame(string name)
    {
        Name = name;
    }

    public string Name { get; }
}

我试图将它移植到 F#,这就是我想出的:

type IGame =
    abstract member Name: string with get

type IMatch<'T when 'T :> IGame> =
    abstract member Games: IEnumerable<'T> with get

type SoccerGame =
    {Name: string}
    interface IGame with
        member this.Name with get() = this.Name

type SoccerMatch =
    { Games: IEnumerable<SoccerGame>}
    interface IMatch<SoccerGame> with
        member this.Games: IEnumerable<SoccerGame> = this.Games

问题是,我需要从我的 C# 应用程序中调用这个 F# 库。之前,在使用 C# 类时,我可以执行以下操作:

var match= new SoccerMatch(new List<SoccerGame>());
IMatch<IGame> interfaceType = match;

但是当我尝试对我的 F# 库做同样的事情时:

var match = new SoccerMatch(new List<SoccerGame>());
IMatch<IGame> interfaceType = match;

我收到以下错误:错误 CS0029 无法将类型“FSharp.SoccerMatch”隐式转换为“FSharp.IMatch”

我认为我的 F# 实现中一定有问题(显然),但是什么?

标签: c#castingf#

解决方案


您的 F# 类型与 C# 类型的行为不同,因为它与 C# 类型不同。C# one 的T参数声明为“out”:

public interface IMatch<out T> where T : IGame

out意味着类型参数T是协变的,这正是允许从SoccerMatch(即IMatch<SoccerGame>) 到IMatch<IGame>.

但是,据我所知,F# 不支持泛型接口中的协变\逆变。多年来一直有人建议,但问题仍然存在。所以你的 F# 接口类似于这个 C# 接口:

public interface IMatch <T> where T : IGame
{
    IEnumerable<T> Games { get; }
}

这将产生相同的编译时错误。


推荐阅读