热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

C#学习教程:c#generic,包括数组和列表?分享

c#generic,包括数组和列表?这是一个非常方便的扩展,适用于任何array:publicstaticTAnyOne(thisT[]ra)whereT:class{intkra

c#generic,包括数组和列表?

这是一个非常方便的扩展,适用于任何array

 public static T AnyOne(this T[] ra) where T:class { int k = ra.Length; int r = Random.Range(0,k); return ra[r]; } 

不幸的是,它不适用于List的任何东西。 这是适用于任何List的相同扩展名

 public static T AnyOne(this List listy) where T:class { int k = listy.Count; int r = Random.Range(0,k); return listy[r]; } 

事实上,是否有一种方法可以一次性推广包含arrayList s的generics? 还是知道不可能?


它发生在我身上,答案甚至可以进一步包含Collection s? 或者确实有下面的专家之一已经实现了??


PS,我很抱歉没有明确提到这是在Unity3D环境中。 “Random.Range”是一个统一到骨骼的function,“AnyOne”调用将100%的游戏引擎读作任何游戏工程师。 这是你为任何游戏项目输入的第一个扩展,并且你经常在游戏代码中使用它(“任何爆炸!”“任何硬币声音效果!”等等!)

显然,它当然可以在任何c#milieu中使用。

事实上,对于你的案例, T[]List之间最合适的公共接口是IReadOnlyList

 public static T AnyOne(this IReadOnlyList list) where T:class { int k = list.Count; int r = Random.Range(0,k); return list[r]; } 

正如在另一个答案中提到的, IList也可以工作,但是好的做法要求您从调用者请求该方法所需的最小function,在本例中是Count属性和只读索引器。

IEnumerable也可以工作,但它允许调用者传递一个非集合迭代器,其中CountElementAt扩展方法效率非常低 – 如Enumerable.Range(0, 1000000) ,数据库查询等。


Unity3D工程师的注意事项:如果你查看IReadOnlyList接口文档的最底层,它可以从.Net 4.5开始使用。 在早期版本的.Net中,您必须使用IList (自2.0起可用)。 Unity在.Net版本上远远落后。 2016年,Unity仅使用.Net 2.0.5。 因此,对于Unity3D,您必须使用IList

T[]List实际上都实现了IList ,它提供了枚举,Count属性和索引器。

 public static T AnyOne(this IList ra) { int k = ra.Count; int r = Random.Range(0,k); return ra[r]; } 

请注意:对于Unity3D环境,具体来说,这是正确的答案。 关于这个答案的进一步改进, IReadOnlyList ,它在Unity3D中不可用。 (关于IEnumerable的(巧妙)扩展甚至覆盖没有计数/可索引性的对象的情况,当然在游戏引擎情况下将是一个独特的概念(例如AnyOneEvenInefficientlyAnyOneEvenFromUnsafeGroups )。)

有些人选择IEnumerable是有趣的,而其他人坚持使用IReadOnlyList

现在让我们说实话。 IEnumerable非常有用,非常有用。 在大多数情况下,您只想将此方法放在某个库中,并将实用程序函数抛出到您认为的集合中,并完成它。 但是,正确使用IEnumerable有点棘手,我在这里指出……

IEnumerable的

让我们假设OP使用Linq并希望从序列中获取随机元素。 基本上他最终得到了来自@Yannick的代码,最终出现在实用程序辅助函数库中:

 public static T AnyOne(this IEnumerable source) { int endExclusive = source.Count(); // #1 int randomIndex = Random.Range(0, endExclusive); return source.ElementAt(randomIndex); // #2 } 

现在,这基本上做的是两件事:

  1. 计算源中元素的数量。 如果源是一个简单的IEnumerable这意味着遍历列表中的所有元素,如果它是f.ex. List ,它将使用Count属性。
  2. 重置可枚举,转到元素randomIndex ,抓住并返回它。

这里有两件事可能会出错。 首先,你的IEnumerable可能是一个缓慢的顺序存储,而做Count会以一种意想不到的方式破坏你的应用程序的性能。 例如,从设备流式传输可能会让您遇到麻烦。 也就是说,你可以很好地争辩说,当这个系列的特征固有的时候会有所期待 – 而且我个人认为这个论点会成立。

其次 – 这可能更重要 – 不能保证你的枚举每次迭代都会返回相同的序列(因此也无法保证你的代码不会崩溃)。 例如,考虑一下看似无辜的代码片段,它可能对测试有用:

 IEnumerable GenerateRandomDataset() { Random rnd = new Random(); int count = rnd.Next(10, 100); // randomize number of elements for (int i=0; i 

第一次迭代(调用Count() ),您可能会生成99个结果。 你选择元素98.接下来你调用ElementAt ,第二次迭代生成12个结果,你的应用程序崩溃。 不酷。

修复IEnumerable实现

正如我们所看到的, IEnumerable实现的问题是你必须经历2次数据。 我们可以通过一次浏览数据来解决这个问题。

这里的’技巧’实际上非常简单:如果我们看过1个元素,我们肯定要考虑返回它。 考虑到所有因素,这是我们将返回的元素的50%/ 50%的可能性。 如果我们看到第三个元素,我们就会有33%/ 33%/ 33%的可能性。 等等。

因此,更好的实现可能是这个:

 public static T AnyOne(this IEnumerable source) { Random rnd = new Random(); double count = 1; T result = default(T); foreach (var element in source) { if (rnd.NextDouble() <= (1.0 / count)) { result = element; } ++count; } return result; } 

在旁注:如果我们使用Linq,我们希望操作使用IEnumerable一次(并且只使用一次!)。 现在你知道为什么了。

使其适用于列表和数组

虽然这是一个巧妙的技巧,但如果我们处理List ,我们的性能现在会变慢,这没有任何意义,因为我们知道有更好的实现可用,因为索引和Count可用于我们。

我们正在寻找的是这个更好的解决方案的共同点 ,我们可以在尽可能多的集合中使用它。 我们最终得到的是IReadOnlyList接口,它实现了我们需要的一切。

由于我们知道 IReadOnlyList的属性,我们现在可以安全地使用Count和索引,而不会冒着崩溃应用程序的风险。

然而,虽然IReadOnlyList看起来很吸引人,但由于某种原因, IList似乎没有实现它&#8230;&#8230;这基本上意味着IReadOnlyList在实践中有点赌博。 在这方面,我非常确定有比IReadOnlyList实现更多的IList IReadOnlyList实现。 因此,最好只支持两种接口。

这引出了我们解决方案:

 public static T AnyOne(this IEnumerable source) { var rnd = new Random(); var list = source as IReadOnlyList; if (list != null) { int index = rnd.Next(0, list.Count); return list[index]; } var list2 = source as IList; if (list2 != null) { int index = rnd.Next(0, list2.Count); return list2[index]; } else { double count = 1; T result = default(T); foreach (var element in source) { if (rnd.NextDouble() <= (1.0 / count)) { result = element; } ++count; } return result; } } 

PS:对于更复杂的场景,请查看策略模式。

随机

@Yannick Motton说你必须小心使用Random ,因为如果你多次调用这样的方法,它就不会是随机的。 使用RTC初始化Random,因此如果您多次创建一个新实例,它将不会更改种子。

一个简单的方法如下:

 private static int seed = 12873; // some number or a timestamp. // ... // initialize random number generator: Random rnd = new Random(Interlocked.Increment(ref seed)); 

这样,每次调用AnyOne时,随机数生成器都会收到另一个种子,即使在紧密循环中它也能正常工作。

总结一下:

所以,总结一下:

最终结果是Just Works。

T[]List都共享相同的接口: IEnumerable

IEnumerable但是,没有Length或Count成员,但有一个扩展方法Count() 。 序列上也没有索引器,因此必须使用ElementAt(int)扩展方法。

有点像:

 public static T AnyOne(this IEnumerable source) { int endExclusive = source.Count(); int randomIndex = Random.Range(0, endExclusive); return source.ElementAt(randomIndex); } 

你可以稍微改变你的定义:

 public static T AnyOne(this IEnumerable ra) { if(ra==null) throw new ArgumentNullException("ra"); int k = ra.Count(); int r = Random.Range(0,k); return ra.ElementAt(r-1); } 

现在,为实现IEnumerable接口的所有类型定义扩展方法。

上述就是C#学习教程:c#generic,包括数组和列表?分享的全部内容,如果对大家有所用处且需要了解更多关于C#学习教程,希望大家多多关注&#8212;编程笔记


推荐阅读
  • 本文分享了一个关于在C#中使用异步代码的问题,作者在控制台中运行时代码正常工作,但在Windows窗体中却无法正常工作。作者尝试搜索局域网上的主机,但在窗体中计数器没有减少。文章提供了相关的代码和解决思路。 ... [详细]
  • 本文介绍了使用Java实现大数乘法的分治算法,包括输入数据的处理、普通大数乘法的结果和Karatsuba大数乘法的结果。通过改变long类型可以适应不同范围的大数乘法计算。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了如何将CIM_DateTime解析为.Net DateTime,并分享了解析过程中可能遇到的问题和解决方法。通过使用DateTime.ParseExact方法和适当的格式字符串,可以成功解析CIM_DateTime字符串。同时还提供了关于WMI和字符串格式的相关信息。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • 本文介绍了Oracle数据库中tnsnames.ora文件的作用和配置方法。tnsnames.ora文件在数据库启动过程中会被读取,用于解析LOCAL_LISTENER,并且与侦听无关。文章还提供了配置LOCAL_LISTENER和1522端口的示例,并展示了listener.ora文件的内容。 ... [详细]
  • C# 7.0 新特性:基于Tuple的“多”返回值方法
    本文介绍了C# 7.0中基于Tuple的“多”返回值方法的使用。通过对C# 6.0及更早版本的做法进行回顾,提出了问题:如何使一个方法可返回多个返回值。然后详细介绍了C# 7.0中使用Tuple的写法,并给出了示例代码。最后,总结了该新特性的优点。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文介绍了在Python3中如何使用选择文件对话框的格式打开和保存图片的方法。通过使用tkinter库中的filedialog模块的asksaveasfilename和askopenfilename函数,可以方便地选择要打开或保存的图片文件,并进行相关操作。具体的代码示例和操作步骤也被提供。 ... [详细]
  • 本文是一位90后程序员分享的职业发展经验,从年薪3w到30w的薪资增长过程。文章回顾了自己的青春时光,包括与朋友一起玩DOTA的回忆,并附上了一段纪念DOTA青春的视频链接。作者还提到了一些与程序员相关的名词和团队,如Pis、蛛丝马迹、B神、LGD、EHOME等。通过分享自己的经验,作者希望能够给其他程序员提供一些职业发展的思路和启示。 ... [详细]
  • 猜字母游戏
    猜字母游戏猜字母游戏——设计数据结构猜字母游戏——设计程序结构猜字母游戏——实现字母生成方法猜字母游戏——实现字母检测方法猜字母游戏——实现主方法1猜字母游戏——设计数据结构1.1 ... [详细]
  • 本文介绍了在Windows环境下如何配置php+apache环境,包括下载php7和apache2.4、安装vc2015运行时环境、启动php7和apache2.4等步骤。希望对需要搭建php7环境的读者有一定的参考价值。摘要长度为169字。 ... [详细]
  • Java学习笔记之面向对象编程(OOP)
    本文介绍了Java学习笔记中的面向对象编程(OOP)内容,包括OOP的三大特性(封装、继承、多态)和五大原则(单一职责原则、开放封闭原则、里式替换原则、依赖倒置原则)。通过学习OOP,可以提高代码复用性、拓展性和安全性。 ... [详细]
  • Linux如何安装Mongodb的详细步骤和注意事项
    本文介绍了Linux如何安装Mongodb的详细步骤和注意事项,同时介绍了Mongodb的特点和优势。Mongodb是一个开源的数据库,适用于各种规模的企业和各类应用程序。它具有灵活的数据模式和高性能的数据读写操作,能够提高企业的敏捷性和可扩展性。文章还提供了Mongodb的下载安装包地址。 ... [详细]
author-avatar
马岱五号_668
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有