使用联合类型

本文介绍了如何在C#中使用F#定义的联合类型,包括创建联合类型实例及判断具体类型的方法。同时,提出了改进联合类型以提高跨语言互操作性的方案。

使用联合类型

 

可以在 C# 中使用联合类型,但是,由于 C# 没有真正意义上的联合类型,因此,在 C# 中使用看上去不漂亮。在这一节,我们将讨论如何在 C# 中使用联合类型,以及作为库设计人员,如何可以决定库是否公开联合类型(虽然,我个人建议避免跨语言公开联合类型)。

第一个例子,我们定义了一个简单的联合类型Quantity,它有两个构造函数,一个包含整数,另一个包含浮点数;还提供一个函数getRandomQuantity(),初始化 Quantity 的新实例。

 

module Strangelights.DemoModule

open System

 

// type that can represent a discrete or continuousquantity

type Quantity =

| Discrete of int

| Continuous of float

 

// initalize random number generator

let rand = new Random()

// create a random quantity

letgetRandomQuantity() =

    match rand.Next(1) with

    | 0 ->Quantity.Discrete (rand.Next())

    | _ ->

       Quantity.Continuous

           (rand.NextDouble() * float_of_int (rand.Next()))

 

虽然,我们提供了getRandomQuantity() 去创建 Quantity 类型的新版本,但是,类型自身也提供了静态方法,去创建组成这个类型的不同构造函数的新实例。这些静态方法在所有联合类型上都可用,它缺省由程序集公开,不需要做任何特别的事情,让编译器去创建它。下面的例子演示了如何从 C# 中使用这些方法:

 

using System;

using Strangelights;

 

static class GetQuantityZeroClass

{

    public static void GetQuantityZero()

    {

        // initialize both aDiscrete and Continuous quantity

        DemoModule.Quantity d = DemoModule.Quantity.Discrete(12);

        DemoModule.Quantity c = DemoModule.Quantity.Continuous(12.0);

    }

}

 

现在,我们已经知道如何从 C# 中创建联合类型,因此,下面最重要的任务就是决定构造函数属于哪一个特定的Quantity 值。可以有三种方法,下面的两段代码,我先讨论前两种方法,第三种方法要放在本节的最后。

第一种方法是可以对值的Tag 属性的分支选择(switch)。这个属性是整数,但是,因为联合类型的编译版本提供的是常量,总是有tag_ 前缀,帮助识别这个整数的含义。因此,如果想使用Tag 属性去找出是哪种类型的Quantity,通常写一个switch 语句,如下面的例子所示:

 

//!!! C# Source !!!

using System;

using Strangelights;

 

static class GetQuantityOneClass

{

    public static void GetQuantityOne()

    {

        // get a randomquantity

        DemoModule.Quantity q = DemoModule.getRandomQuantity();

 

        // use the .Tagproperty to switch over the quatity

        switch (q.Tag)

        {

            case DemoModule.Quantity.tag_Discrete:

                Console.WriteLine("Discretevalue: {0}",q.Discrete1);

                break;

            case DemoModule.Quantity.tag_Continuous:

                Console.WriteLine("Continuousvalue: {0}",q.Continuous1);

                break;

        }

    }

}

 

这个例子的运行的结果如下:

 

Discrete value: 65676

 

如果我们愿意,联合类型的编译形式也提供了一系列方法,前缀都是Is,用测试一个值是否属于联合类型中的特定构造函数。例如,在联合类型Quantity 中,有两个方法,IsDiscrete() 和IsContinuous(),测试Quantity 是属于Discrete,还是Continuous。下面的例子就演示了如何使用:

 

//!!! C# Source !!!

using System;

using Strangelights;

 

static class GetQuantityTwoClass

{

    public static void GetQuantityTwo()

    {

        // get a randomquantity

        DemoModule.Quantity q = DemoModule.getRandomQuantity();

        // use if ... elsechain to display value

        if (q.IsDiscrete())

        {

            Console.WriteLine("Discretevalue: {0}",q.Discrete1);

        }

        else if (q.IsContinuous())

        {

            Console.WriteLine("Continuousvalue: {0}",q.Continuous1);

        }

    }

}

 

示例的运行结果如下:

 

Discrete value: 2058

 

因为执行模式匹配所需的代码量过大,所以,这两种方法都不特别令人满意;且还有风险,如果写的代码如下所示,检测值是否是Discrete,但错误地使用了Continuous1 属性,就会出错,导致了NullReferenceException 异常。

 

DemoModule.EasyQuantity q = DemoModule.getRandomEasyQuantity();

if (q.IsDiscrete())

{

    Console.WriteLine("Discretevalue: {0}", q.Continuous1);

}

 

为给库用户提供更多的保障,为联合类型添加成员,执行模式匹配,是一个好办法。下面的例子修改了Quantity 类型,产生新类型EasyQuantity,添加两个成员,把类型转换成整数或浮点数:

 

module Strangelights.ImprovedModule

open System

 

//type that can represent a discrete or continuous quantity

//with members to improve interoperability

type EasyQuantity =

|Discrete of int

|Continuous of float

  // convert quantity to a float

  member x.ToFloat() =

    match x with

    | Discrete x -> float x

    | Continuous x -> x

  // convert quantity to a integer

  member x.ToInt() =

    match x with

    | Discrete x -> x

    | Continuous x -> int x

 

//initalize random number generator

let rand = new Random()

 

//create a random quantity

letgetRandomEasyQuantity() =

  match rand.Next(1) with

  | 0 -> EasyQuantity.Discrete (rand.Next())

  | _ ->

        EasyQuantity.Continuous

          (rand.NextDouble() * float(rand.Next()))

 

这样,库用户既可把值转换成整数,也可以转换成浮点数,而不必担心模式匹配,就像下面的例子:

 

//!!! C# Source !!!

using System;

using Strangelights;

 

class GetQuantityThreeClass

{

    public static void GetQuantityThree()

    {

        // get a randomquantity

        ImprovedModule.EasyQuantity q = ImprovedModule.getRandomEasyQuantity();

        // convert quantityto a float and show it

        Console.WriteLine("Value as afloat: {0}",q.ToFloat());

    }

}

[

所有的示例都没有测试。

]

在 Skulpt 的 Python 3.7 环境中,确实**不包含完整的 `typing` 模块**,这是由于 Skulpt 为了轻量化和在浏览器中运行的限制,只实现了部分标准库。 如果你希望在 Skulpt 中使用**联合类型注解(Union Types)**,但又受限于 `typing` 模块不可用,有以下几种替代方案: --- ### ✅ 方法一:使用字符串注解(String-based Type Hints) 你可以使用 Python 支持的**字符串类型注解**方式来表示联合类型,这样即使没有 `typing` 模块,也能写出清晰的类型提示。 ```python def greet(name: 'str | None') -> 'None': if name is None: print("Hello, Guest!") else: print(f"Hello, {name}!") greet("Alice") # 输出: Hello, Alice! greet(None) # 输出: Hello, Guest! ``` > 这种方式只是用于文档和 IDE 提示,不会在运行时做类型检查。 --- ### ✅ 方法二:使用 `Optional` 的字符串表示(模拟) 由于 `Union` 和 `Optional` 无法直接导入,可以使用字符串 `'Optional[type]'` 来表示: ```python def get_user_name() -> 'Optional[str]': return None name = get_user_name() print(name) ``` > 这种方式仅用于文档说明,Skulpt 不会做类型检查。 --- ### ✅ 方法三:使用注释方式(Python 2 风格) 如果你需要兼容性更强的方式,可以使用注释来说明类型: ```python def process_data(data): # type: (int | str) -> None print(f"Data is: {data}") process_data(42) # int process_data("hello") # str ``` 这种方式是 Python 的旧风格类型注解(PEP 484),被许多 IDE 和类型检查工具支持。 --- ### ✅ 方法四:手动类型检查(运行时逻辑) 如果你确实需要在 Skulpt 中实现运行时类型判断,可以手动添加类型检查: ```python def process_value(value): if isinstance(value, int) or isinstance(value, str): print(f"Processing value: {value}") else: raise TypeError("Expected int or str") process_value(100) process_value("test") # process_value(3.14) # 会抛出 TypeError ``` --- ### 总结 | 方法 | 是否依赖 typing | 是否运行时检查 | 适用场景 | |------------------|------------------|----------------|------------------------------| | 字符串类型注解 | 否 | 否 | 文档说明、IDE 提示 | | 注释方式类型提示 | 否 | 否 | 向后兼容、IDE 支持 | | 手动类型检查 | 否 | 是 | 实现运行时安全 | | 模拟 Optional 注解 | 否 | 否 | 表示可能为 None 的类型 | ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值