如何使特定类型属于一个类型的家庭?

时间:2016-07-04 20:55:53

标签: .net f#

我试图制作一个包含多种可能类型的集合。这是我希望它看起来的一个例子:

type Position = Vector2
type Velocity = Vector2
type Appearance = String

type Component = Position | Velocity | Appearance

let components = List<Dictionary<string, Component>>()
let pos = Dictionary<string, Position>()

components.Add(pos) // Type "Position" does not match with type "Component"

我想声明仍然适合一般类型的特定类型。有没有办法可以像这样写我的代码?有没有更惯用的方法呢?

2 个答案:

答案 0 :(得分:6)

您的代码中有很多内容:

输入缩写Link

type Position = Vector2
type Velocity = Vector2
type Appearance = String

类型缩写只是为现有类型定义另一个名称,左侧和右侧的类型和精确等效,可以互换使用。

为了举一个与这个问题相关的例子,在F#中有一个标准.NET List的类型缩写,它叫做ResizeArray。它的定义如下:

type ResizeArray<'T> = System.Collections.Generic.List<'T>

它可以节省您必须打开System.Collections.Generic才能使用它,这有助于避免与F#中的list类型混淆,但除了为其添加新名称之外,它不会做任何其他事情。现有的类型。

受歧视的工会Link

type Component = Position | Velocity | Appearance

这里有一个名为Component的单一类型,您可以将其视为具有三个构造函数的单一类型:PositionVelocityAppearance。您还可以通过模式匹配使用相同的三种情况再次解构类型。

e.g。

match comp with
|Position -> ..
|Velocity -> ..
|Appearance -> ..

希望您声明的类型缩写Position与您声明为Position类型的一部分的联合案例Component没有任何联系,这应该不足为奇了。它们完全相互独立。

Position表示Vector2Component是完全独立的联合类型。

假设您想要一个可能包含多个内容的Component类型,则需要将一些值与案例相关联。以下是创建此类歧视联盟的示例:

type Component = 
    | Position of Vector2
    | Velocity of Vector2
    | Appearance of string

现在,让我们看看下一个问题。

如果我们删除类型缩写并使用我们新的Discriminated Union

尝试其余代码
let components = List<Dictionary<string, Component>>()
let pos = Dictionary<string, Position>()

我们现在有一个新错误:

  

未定义类型Position

好吧,请记住我之前所说的关于Component的内容。 Component是类型,Position不是类型,它是Component的联合案例。

如果您想要包含这些选项中相同选项的完整词典,您可能最好将定义更改为以下内容:

type ComponentDictionary =
    |PositionDictionary of Dictionary<string, Vector2>
    |VelocityDictionary of Dictionary<string, Vector2>
    |AppearanceDictionary of Dictionary<string, string>

然后你可以创建ResizeArray / List这些。

let components = ResizeArray<ComponentDictionary>()

现在,为了填充此集合,我们只需要为ComponentDictionary使用适当的case构造函数

let pos = PositionDictionary (Dictionary<string, Vector2>())

现在,pos的类型为ComponentDictionary,因此我们可以将其添加到组件中:

components.Add(pos) // No error here!

答案 1 :(得分:0)

TheInnerLight的答案非常完整。我想补充一点,使用纯F#类型(列表,地图或字典)可能有一些优势,而不是通用的.Net列表和字典。当然,您可能确实需要.NET泛型集合。这是一个使用map(或dict)并创建各种Component map列表的简单示例:

type Vector2 = float * float

type Component = 
    | Position of Vector2
    | Velocity of Vector2 
    | Appearance of string

type Components = list<Map<string,Component>> // this is not strictly necessary

let pos = Position(10.,20.)
let app = Appearance "here"

let compMap1= Map.empty<string,Component> // define an empty map for Component
let compMap1 = compMap1.Add("key1",pos).Add("key2",app) // or: let compMap1' = dict(["key1",pos;"key2",app])
let compMap2 = ["key3",pos;"key4",pos] |> Map.ofList // you can create map from a list
let (components:Components) = [compMap1;compMap2] // make a list of your maps
let components' = [compMap1;compMap2] // or just use a generic F# list
(*
val components' : Map<string,Component> list =
  [map [("key1", Position (10.0, 20.0)); ("key2", Appearance "here")];
   map [("key3", Position (10.0, 20.0)); ("key4", Position (10.0, 20.0))]]
*)

如果只需要一个包含各种组件类型的列表(或数组,或其他),您就可以这样做了:

let compList = [pos;app]
//val compList : Component list = [Position (10.0, 20.0); Appearance "here"]