f# - 实现通用接口:构造导致代码比类型注释所指示的更通用
问题描述
我需要实现这样的接口:
interface IEvent<T> : IEvent {
T Data { get; }
}
public interface IEvent
{
Guid Id { get; set; }
long Version { get; set; }
long Sequence { get; set; }
object Data { get; }
Guid StreamId { get; set; }
string StreamKey { get; set; }
DateTimeOffset Timestamp { get; set; }
string TenantId { get; set; }
Type EventType { get; }
string EventTypeName { get; set; }
string DotNetTypeName { get; set; }
}
这就是我想出的:
type WrappedEvent<'T>(x: 'T) =
interface Events.IEvent with
member val Data = x with get
member val DotNetTypeName = null with get, set
member val EventType = null
member val EventTypeName = null with get, set
member val Id = Guid.Empty with get, set
member val Sequence = int64 (0) with get, set
member val StreamId = Guid.Empty with get, set
member val StreamKey = null with get, set
member val TenantId = null with get, set
member val Timestamp = DateTimeOffset.MinValue with get, set
member val Version = int64 (0) with get, set
WrappedEvent
像这样使用:
let MapToSubtype subtype =
match subtype with
| CustomerRegistered registeredCustomer -> WrappedEvent<CustomerRegisteredEvent> registeredCustomer :> Events.IEvent
| CustomerDeleted deletedCustomer -> WrappedEvent<CustomerDeletedEvent> deletedCustomer :> Events.IEvent
<'T>
编译器为at引发错误WrappedEvent<'T>
This type parameter has been used in a way that constrains it to always be 'obj'
This code is less generic than required by its annotations because the explicit type variable 'T' could not be generalized.
It was constrained to be 'obj'.
Data
在 at显示警告member val Data
:
This construct causes code to be less generic than indicated by the type annotations.
The type variable 'T has been constrained to be type 'obj'.
我该如何解决这个问题?
更新:
如果我正在实现这样的接口interface Events.IEvent<'T> with
(使用泛型参数),我会收到此错误:
No implementation was given for 'Events.IEvent.get_Data() : obj'.
Note that all interface members must be implemented and listed under an appropriate 'interface' declaration, e.g. 'interface ... with member ...'.
解决方案
首先,您在这里实现了两个接口,而不是一个,IEvent<T>
以及它的基本接口IEvent
。因此,您需要添加两个interface ... with
块 - 每个块一个:
type WrappedEvent<'T>(x: 'T) =
interface Events.IEvent<'T> with
member val Data = x with get
interface Events.IEvent with
member val Data = x with get
member val DotNetTypeName = null with get, set
member val EventType = null
...
其次,在实现IEvent
接口时,注意其Data
成员的类型:它是obj
. 因此,如果您使用 初始化该成员x
,则x
必须具有 type obj
。这就是你原来的错误的来源:x
被使用的方式会使其被限制在obj
.
要解决此问题,您需要在使用它进行初始化之前向下x
转换:obj
Data
interface Events.IEvent with
member val Data = x :> obj with get
可是等等!这仍然行不通。问题是,member val
不仅声明了一个属性,还声明了一个支持字段。现在您要声明两个不同的支持字段,它们都命名为Data
,但类型不同。(另外,顺便说一句,您实际上并不需要单独的支持字段,对吗?因为您已经有了x
)
所以为了解决这个问题,让它们成为没有支持字段的属性:
interface Events.IEvent<'T> with
member self.Data with get() = x
interface Events.IEvent with
member self.Data with get() = x :> obj
...
推荐阅读
- firebase - 如何阻止某些 IP 地址访问您在 Firebase 托管上的网站
- c - 我无法在我的 Windows 桌面上编译 GMP 库(用于 c 或 c++)
- dataframe - 如何使来自 for 循环的数组在 Julia 中全局可用?
- python - 训练准确度分数不同于后续测试准确度分数
- r - 如何表示组中的两个最高观察值(有两个条件)?
- r - confint() 是否以对数赔率或赔率返回 CI?
- wordpress - 用另一个替换 WordPress 主题的搜索栏
- python - 如何获取用户输入并将其替换为求解方法?
- sql - 逗号分隔字段上的表值函数 (TVF)
- laravel - 使用 Vue Js 下载文本文件