首页 > 解决方案 > 实现通用接口:构造导致代码比类型注释所指示的更通用

问题描述

我需要实现这样的接口:

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 ...'.

标签: f#

解决方案


首先,您在这里实现了两个接口,而不是一个,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转换:objData

    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
        ...

推荐阅读