第四章 简介

方法的结构重复问题

我们在上一篇正式整理完毕,从这一篇开始,我们要再次进入学习收集示例阶段了。

那么我们学什么呢?当然是学习设计工具,也就是在上篇中提到的关键知识点。这些关键知识点,大部分来自于 C# 语法。

不过在此之前,我们先实现一个功能,这个功能是,传入几个数字,随机取出其中一个数字。

比如传入 1,3,5,7 从这四个数字中随机取出一个数字出来。

其实现很简单 代码如下:

using UnityEngine;

namespace QFramework
{
public partial class MathUtil
{
public static int GetRandomValueFrom(int[] values)
{
return values[Random.Range(0, values.Length)];
}
}
}

传入一个,int 数组,从 int 类型数组中随机取一个数值进行返回。

用法也很简单,使用代码如下:

var randomValue = GetRandomValueFrom(new int[] {5, 100, -1, -3, 5});

但是随着时间发展,我们有时候会需要随机获取其他类型值,比如从一个 float 类型的数组中随机取回一个 float 值,或者是从 string 类型的数组中取回一个值。

难道我们要每个类型都实现一次嘛? 如果都实现一次的话,会增多很多重复结构的代码,注意看这里是重复的结构。

什么叫重复的结构,以下代码就是三个重复结构的方法。

public static int GetRandomValueFrom(int[] values)
{
return values[Random.Range(0, values.Length)];
} public static string GetRandomValueFrom(string[] values)
{
return values[Random.Range(0, values.Length)];
} public static float GetRandomValueFrom(float[] values)
{
return values[Random.Range(0, values.Length)];
}

虽然对初学者来说,知道把方法做成工具类已经很不容易了,但是以上这样的代码肯定是不行的。

所以就要学习新的知识,但是笔者接下来要说的这个知识大家应该很熟悉了。

就是面试中常问的:面向对象的特性?

答:封装、继承、多态。

而我们要用其中的继承特性。

我们先复习一下继承:B 继承 A,那么 B 对象就可以拥有 A 中的 public、protect 方法和成员变量。

复习完了。。。

继承的本质是,什么是什么的问题,比如 B 继承 A,那么我们可以使用 A 变量去接收 B 对象。

代码如下:

class A
{
} class B : A
{
} A a = new B();

这样的的代码是没问题的。

虽然以上代码和多态性有一点关系,不过我们只要记住,父类的变量可以接收子类的实例这个结论就好了。

那么对我们的问题有什么帮助呢?

我么再来看下问题代码:

public static int GetRandomValueFrom(int[] values)
{
return values[Random.Range(0, values.Length)];
} public static string GetRandomValueFrom(string[] values)
{
return values[Random.Range(0, values.Length)];
} public static float GetRandomValueFrom(float[] values)
{
return values[Random.Range(0, values.Length)];
}

int、string、float 如果有一个共同的父类,那么就可以用一个共同的父类,作为参数的类型。

那么 int、string、float 有没有共同的父类?答案是有的,它们共同继承了 object 类。不止是int、string、float,C# 中的所有类型都继承了 object 类。

我们有了答案,可以进行更改了,改完的代码如下:

public static object GetRandomValueFrom(object[] values)
{
return values[Random.Range(0, values.Length)];
}

我们看下使用的代码

var intRandomValue = (int)GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = (string)GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = (float)GetRandomValueFrom(new float[]{ 0.1f,0.2f });

使用的代码虽然比较难看(1.强制类型转换;2.再加上每次传入参数都要构造数组;)

使用上有一点麻烦,不过还好,我们最起码解决了结构重复的问题,而且我们还复习了一下继承。

当然除了使用 object 接收,还有更好的方法-使用泛型。之所以先介绍 object ,是因为,笔者在没接触泛型之前,都是这么搞的,这样搞其实还有个问题,就是值类型转引用类型会造成效率问题,不过这些问题下一篇再解决,更重要的是,我们今天的问题得到了进展,这是很值得开心的。

泛型:结构复用利器

在上一篇我们使用 object 解决了方法结构重复的问题,而在文章的尾部又提了一下更好的方法,就是泛型。

泛型对很多初学者来说是比较高级的概念,这里呢我们顺便复习一下泛型。

泛型是什么呢?对于方法来说,方法结构中的部分或全部类型都可以先不进行定义,而是到调用方法的时候再去定义。

我们的 GetRandomValue 的目前代码如下 :

public static object GetRandomValueFrom(object[] values)
{
return values[Random.Range(0, values.Length)];
}

测试代码如下:

var intRandomValue = (int)GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = (string)GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = (float)GetRandomValueFrom(new float[]{ 0.1f,0.2f });

将 object 改成泛型后如下:

public static T GetRandomValueFrom<T>(T[] values)
{
return values[Random.Range(0, values.Length)];
}

测试代码如下:

var intRandomValue = GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = GetRandomValueFrom(new float[]{ 0.1f,0.2f });

从测试代码中可以比较出来,使用泛型之后的代码确实好用了很多。

大家思考下泛型是不是这样的?结构中的部分或全部类型都可以先不进行定义,而是到调用的时候再去定义。

我们有收获了一个法宝泛型。要说方法是逻辑上的复用,那么泛型就是结构上的复用。两大复用法宝。

关于泛型就先介绍到这里,我们以后还会遇到使用泛型的时候的。

在看下当前的测试代码:

var intRandomValue = GetRandomValueFrom(new int[]{1,2,3});
var stringRandomValue = GetRandomValueFrom(new string[]{"asdasd","123123"});
var floatRandomValue = GetRandomValueFrom(new float[]{ 0.1f,0.2f });

目前比较麻烦的是数组构造代码了。

这个也是有办法搞定的。我们把方法的实现改成如下:

public static T GetRandomValueFrom<T>(params T[] values)
{
return values[Random.Range(0, values.Length)];
}

大家注意下,参数前边加上了一个 params 关键字。这个是什么意思呢?

意思是 GetRandomValueFrom 可以传任意数量的参数。

我们先看下测试代码,测试代码呢其实可以改成如下:

var intRandomValue = GetRandomValueFrom(1, 2, 3);
var stringRandomValue = GetRandomValueFrom("asdasd", "123123");
var floatRandomValue = GetRandomValueFrom(0.1f, 0.2f);

是不是清爽了很多?这就是 params 的用法。

而通过 params 修饰的 values 参数来说,如果传入的是一个数组,那么 values 本身就是这个数组,如果传入的是若干个值,那么 values 中就包含了这若干个值。

总结一句话,就是可以传一整个数组,也可以传若干个参数,设计得非常人性化。

到此呢,我们的第九个示例就已经非常完善了。

全部代码如下:

using UnityEngine;

namespace QFramework
{
public partial class MathUtil
{
#if UNITY_EDITOR
[UnityEditor.MenuItem("QFramework/9.从若干个值中随机取出一个值", false, 10)]
#endif
private static void GetRandomValueFromMenuClicked()
{
Debug.Log(GetRandomValueFrom(1, 2, 3));
Debug.Log(GetRandomValueFrom("asdasd", "123123"));
Debug.Log(GetRandomValueFrom(0.1f, 0.2f));
} public static T GetRandomValueFrom<T>(params T[] values)
{
return values[Random.Range(0, values.Length)];
}
}
}

代码执行结果如下:

编译之后菜单如下

目录结构如下

我们进行一次导出,这次导出就正常导出就好。

今天的内容就到这里,我们下一篇再见,拜拜~。

转载请注明地址:凉鞋的笔记:liangxiegame.com

更多内容

Unity 游戏框架搭建 2019 (三十九、四十一) 第四章 简介&方法的结构重复问题&泛型:结构复用利器的更多相关文章

  1. Unity 游戏框架搭建 2019 (三十、三十一) MenuItem 显示顺序问题 & 类的提取

    在上一篇,我们得出了两个核心的学习思路: 根据问题去学习,并收集. 主动学习,并思考适用场景. 我们今天解决 MenuItem 显示顺序问题. 目前 MenuItem 显示如图所示: 我们来看下 Me ...

  2. Unity 游戏框架搭建 2019 (三十二、三十三) 类的命名 & 代码文件命名

    昨天我们完成了第八个示例的第二个 MenuItem 菜单顺序的调整. 我们今天再往下接着调整. 我们来看下接下来的 MenuItem 代码如下: [MenuItem("QFramework/ ...

  3. # Unity 游戏框架搭建 2019 (三十四、三十五) 9 ~ 10 示例整理

    第九个示例 目前代码如下: using UnityEngine; #if UNITY_EDITOR using UnityEditor; #endif namespace QFramework { p ...

  4. Unity 游戏框架搭建 2019 (三十六~三十八) partial与public

    在上一篇,我们把菜单的顺序从头到尾整理了一遍.在整理菜单顺序的过程中,记录了一个要做的事情. 要做的事情: (完成) 备份:导出文件,并取一个合理的名字. 整理完菜单顺序后,学习新的知识,解决随着示例 ...

  5. Unity 游戏框架搭建 2019 (二十九) 方法所在类命名问题诞生的原因

    我们在整理阶段解决了一些意外的问题.但是这些问题仅仅只是被解决而已,我们并没有去思考过这些问题是为什么产生的?以及在以后我们如何去避免这些问题的产生? 方法所在类的命名问题,最后我们通过方法分类解决了 ...

  6. Unity 游戏框架搭建 2019 (五十、五十一) 消息机制小结&MonoBehaviourSimplify 是框架?

    我们花了 5 篇文章学习了消息机制的方方面面.并且完成了一个简易消息机制,之后集成到了我们的 MonoBehaviourSimplify 里. 现在 MonoBehaviourSimplify 有一点 ...

  7. Unity 游戏框架搭建 2019 (十三~十五) 接下来要学什么?& 第九个示例

    在之前的两篇中,我们使用 public 静态方法对之前的内容进行了一个抽取,有了 public 静态方法这个工具,我们的学习行为也发生了一点变化. 在没使用 public 关键字之前呢,每一个示例仅仅 ...

  8. Unity 游戏框架搭建 2019 (二十五) 类的第一个作用 与 Obselete 属性

    在上一篇我们整理到了第七个示例,我们今天再接着往下整理.我们来看第八个示例: #if UNITY_EDITOR using UnityEditor; #endif using UnityEngine; ...

  9. Unity 游戏框架搭建 2019 (二十六) 第一轮整理完结

    昨天呢我们把第八个示例整理完了.整理之后学习了类的第一作用:方法的集合,还有 Obselete 这个 API.并且在进行整理的时候贯彻了我们新的约定和规则:先确保功能有效,再去做变更和删除. 今天我们 ...

随机推荐

  1. Linux - Ubuntu18.04下更改apt源为阿里云源

    进入apt目录,备份原来的源地址 cd /etc/apt mv ./source.list ./source.list.bak 修改源文件source.list vim source.list 更换阿 ...

  2. 实验十--- MySQL过程式数据库对象

    实验十 MySQL过程式数据库对象 一.  实验内容: 1. 存储过程的创建和调用 2. 存储函数的创建和调用 3. 触发器的创建和触发 4. 事件的创建和修改 一.  实验项目:员工管理数据库 用于 ...

  3. 面试官:JavaScript 原始数据类型 Symbol 有什么用?

    以前提到 JavaScript 原始数据类型时,我们知道有Number,String,Null,Boolean,Undefined这几种.ES6 引入了新的基本数据类型Symbol和BigInt.今天 ...

  4. Step by Step!教你如何在k3s集群上使用Traefik 2.x

    本文来自边缘计算k3s社区 作者简介 Cello Spring,瑞士人.从电子起步,拥有电子工程学位.尔后开始关注计算机领域,在软件开发领域拥有多年的工作经验. Traefik是一个十分可靠的云原生动 ...

  5. 非参数估计——核密度估计(Parzen窗)

    核密度估计,或Parzen窗,是非参数估计概率密度的一种.比如机器学习中还有K近邻法也是非参估计的一种,不过K近邻通常是用来判别样本类别的,就是把样本空间每个点划分为与其最接近的K个训练抽样中,占比最 ...

  6. Git应用详解第二讲:Git删除、修改、撤销操作

    前言 前情提要:Git应用详解第一讲:Git分区,配置与日志 在第一讲中我们对Git进行了简单的入门介绍,相信聪明的你已经了解Git的基本使用了. 这一讲我们来进一步深入学习Git应用,着重介绍Git ...

  7. "字母全变小写"组件:<lowercase> —— 快应用组件库H-UI

     <import name="lowercase" src="../Common/ui/h-ui/text/c_text_lowercase">& ...

  8. RESTful API设计的点

    RESTful API 前言 一直在使用RESTful API,但是好像概念还是很模糊的,总结下使用到的点 设计的规范 协议 API与用户的通信协议,总是使用HTTPs协议. 域名 应该尽量将API部 ...

  9. <context:component-scan base-package=""> 与 <context:annotation-config 区别

    <context:component-scan base-package=""> <context:annotation-config (2012-11-16 2 ...

  10. web.page.regexp用法(全网唯一)

    前言 因为这个东西“web.page.regexp”,差点把自己杀了.一点都不夸张,这将近30度的天气,办公室不开空调,又要闷,还要带着口罩,躁动的很.加上这个鬼东西“web.page.regexp” ...