只有你能 new 出来!.NET 隐藏构造函数的 n 种方法(Builder Pattern / 构造器模式)

栏目: ASP.NET · 发布时间: 5年前

内容简介:如果你给类写了一个公有构造函数,那么这个类就能被其他开发者 new 出来。如果你不想让他们 new 出来,把构造函数然而还有更多奇怪的方式来隐藏你类的构造方法。有些类型,只有组件的设计者才知道如何正确创建其类型的实例,多数开发者都无法正确将其创建出来。典型的如

如果你给类写了一个公有构造函数,那么这个类就能被其他开发者 new 出来。如果你不想让他们 new 出来,把构造函数 private 就好了呀。

然而还有更多奇怪的方式来隐藏你类的构造方法。

为什么要隐藏构造函数?

有些类型,只有组件的设计者才知道如何正确创建其类型的实例,多数开发者都无法正确将其创建出来。典型的如 string :绝大多数开发者都不能正确创建出 string 的实例,但通过写一个字符串由编译器去创建,或者使用 StringBuilder 来构造则不容易出错。

再或者,我们只希望开发者使用到某个抽象的实例,而不是具体的类型,那么这个时候开发者也需要有方法能够拿到抽象接口的实例。我们可能会使用工厂或者某些其他的方法让开发者在不知道具体类型的时候获取到抽象类型的实例。

这正是构造器模式的典型应用场景。在维基百科中对它适用性的描述为:

在以下情况使用生成器模式:

  • 当创建复杂对象的算法应该独立于该对象的组成部分以及它们的装配方式时;
  • 当构造过程必须允许被构造的对象有不同的表示时。

详见: 生成器模式 - 维基百科,自由的百科全书

接下来,我们使用一些奇怪的方式来创建对象的实例,完完全全把构造函数隐藏起来。

隐式转换和显式转换

典型的像 long a = 1;bool? b = true 这都是语法级别的隐式转换。这真的只是语法级别的隐式转换,实际上这两个都是编译器原生支持,编译时即已转换为真实的类型了。

[System.Runtime.Versioning.NonVersionable]
public static implicit operator Nullable<T>(T value)
{
    return new Nullable<T>(value);
}

[System.Runtime.Versioning.NonVersionable]
public static explicit operator T(Nullable<T> value)
{
    return value.Value;
}

于是我们可以考虑写一个神奇的类,其创建是通过隐式转换来实现的:

Fantastic fantastic = "walterlv";
Console.WriteLine(fantastic);

以上代码的输出是 walterlv is fantastic

namespace Walterlv.Demo.Patterns
{
    public class Fantastic
    {
        private readonly string _value;
        private Fantastic(string value) => _value = value;
        public static implicit operator Fantastic(string value) => new Fantastic(value);
        public override string ToString() => $"{_value ?? "null"} is fantastic.";
    }
}

而使用显式转换,我们还可以写出更奇怪的代码来。比如下面这个,我们的实例是通过强制转换一个 null 来实现的:

Fantastic fantastic = (IFantastic) null;
Console.WriteLine(fantastic);

以上代码的输出是 ` is fantastic` 字符串。呃……前面有个空格。

namespace Walterlv.Demo.Patterns
{
    public struct Fantastic
    {
        private readonly IFantastic _value;
        private Fantastic(IFantastic value) => _value = value;
        public static implicit operator Fantastic(IFantastic value) => new Fantastic(value);
        public override string ToString() => $"{_value} is fantastic.";
    }

    public class IFantastic
    {
    }
}

那个 IFantastic 必须得是一个类,而不能是接口,因为隐式转换不能从接口转,也不能转到接口。

只有你能 new 出来!.NET 隐藏构造函数的 n 种方法(Builder Pattern / 构造器模式) ▲ 不能定义从接口进行的隐式转换

运算符重载

使用运算符重载,也可以让类型实例的构造隐藏起来。比如下面的 Scope 类型,从字符串创建,然后通过与不同的字符串进行位或运算来得到其他的 Scope 的实例。

Scope scope = "A";
var full = scope | "B" | "C";
Console.WriteLine(full);

当然这段代码也少不了隐式转换的作用。

以上 Scope 类型的实现在 github 上开源,其表示 OAuth 2.0 中的 Scope

ERMail/Scope.cs

关于运算符重载的更多内容,可以参考我的另外两篇文章:

本文会经常更新,请阅读原文: https://walterlv.com/post/hide-your-constructor.html ,以避免陈旧错误知识的误导,同时有更好的阅读体验。

只有你能 new 出来!.NET 隐藏构造函数的 n 种方法(Builder Pattern / 构造器模式) 本作品采用 知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议 进行许可。欢迎转载、使用、重新发布,但务必保留文章署名 吕毅 (包含链接:https://walterlv.com ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。如有任何疑问,请 与我联系 (walter.lv@qq.com)


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Go语言实战

Go语言实战

威廉·肯尼迪 (William Kennedy)、布赖恩·克特森 (Brian Ketelsen)、埃里克·圣马丁 (Erik St.Martin) / 李兆海 / 人民邮电出版社 / 2017-3-1 / CNY 59.00

Go语言结合了底层系统语言的能力以及现代语言的高级特性,旨在降低构建简单、可靠、高效软件的门槛。本书向读者提供一个专注、全面且符合语言习惯的视角。Go语言实战同时关注语言的规范和实现,涉及的内容包括语法、类型系统、并发、管道、测试,以及其他一些主题。一起来看看 《Go语言实战》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具