概述:在真正的对象化开发项目中,我们通常会将常用的业务实体抽象为特定的类,如Employee、Customer、Contact等,而多数的类之间会存在着相应的关联或依存关系,如Employee和Customer通过Contact而产生关联、Contact是依赖于Employee和Customer而存在的。在实际的对象应用模块中,可能会有这样的需求:获得一组客户对象(即Customers集合类的实例,如customers),指向其中一个Customer对象(如customers[i]),通过访问这个Customer对象的属性Name(customers[i].Name)和Contacts(如customers[i].Contacts)来查询客户的姓名和与该客户的联络记录,甚至遍历Contacts对象,查找该客户的某次联络摘要(即customers.[i].contacts[x].Summary)。为满足以上集合类的需求,对照.NET Framework 的平台实现,不难发现.NET在Collections命名空间下提供了一系列实现集合功能的类,并且根据适用环境的不同为开发者提供灵活多样的选择性:如通过索引访问使用广泛的ArrayList 和 StringCollection;通常在检索后被释放的先进先出的Queue和后进先出Stack;通过元素键对其元素进行访问Hashtable、SortedList、ListDictionary 和 StringDictionary;通过索引或通过元素键对其元素进行访问的NameObjectCollectionBase 和 NameValueCollection;以及具有集合类的特性而被实现在System.Array下的Array类等。本文将通过实现具有代表性的 “集合类”的两种典型途径,分析对比不同实现方式的差异性与适用环境,让大家了解和掌握相关的一些技术,希望为大家的学习和开发工作起到抛砖引玉的作用(注:作者的调试运行环境为.NET Framework SDK 1.1)。
1.采用从CollectionBase抽象基类继承的方式实现Customers集合类:
首先需要创建为集合提供元素的简单类Customer:
/// <summary>
/// 描述一个客户基本信息的类
/// </summary>
public class Customer
{
/// <summary>
/// 客户姓名
/// </summary>
public string Name;
/// <summary>
/// 描述所有客户联络信息的集合类
/// </summary>
//public Contacts Contacts=new Contacts();
/// <summary>
/// 不带参数的Customer类构造函数
/// </summary>
public Customer()
{
System.Console.WriteLine("Initialize instance without parameter");
}
/// <summary>
/// 带参数的Customer类构造函数
/// </summary>
public Customer(string name)
{
Name=name;
System.Console.WriteLine("Initialize instance with parameter");
}
}
以上就是Customer类的简单框架,实用的Customer类可能拥有更多的字段、属性、方法和事件等。值得注意的是在Customer类中还以公共字段形式实现了对Contacts集合类的内联,最终可形成Customer.Contacts[i]的接口形式,但这并不是最理想的集合类关联方式,暂时将它注释,稍后将详加分析,这个类的代码重在说明一个简单类(相对于集合类的概念范畴)的框架;另外,该类还对类构造函数进行了重载,为声明该类的实例时带name参数或不带参数提供选择性。
接下来看我们的第一种集合类实现,基于从CollectionBase类派生而实现的Customers类:
/// <summary>
/// Customers 是Customer的集合类实现,继承自CollectionBase
/// </summary>
public class Customers: System.Collections.CollectionBase
{
public Customers()
{
}
/// <summary>
/// 自己实现的Add方法
/// </summary>
/// <param name="customer"></param>
public void Add(Customer customer)
{
List.Add(customer);
}
/// <summary>
/// 自己实现的Remove方法
/// </summary>
/// <param name="index"></param>
public void Remove(int index)
{
if (index > Count - 1 || index < 0)
{
System.Console.WriteLine("Index not valid!");
}
else
{
List.RemoveAt(index);
}
}
}
以Customers集合类为例,结合集合辅助技术,希望大家能了解掌握以下知识:
从CollectionBase继承实现集合类
Customers类采用从CollectionBase继承的方式,不再需要在类内声明一个作为Customer集合容器的List对象,因为CollectionBase类已经内置了一个List对象,并已经实现了Count、Clear、RemoveAt等等IList的重要接口(具体请参照MSDN中的CollectionBase 成员),只需要用户显示实现Add、Remove、IndexOf、Insert等等接口,代码中仅简单实现了Add方法和Remove方法的整参数版本作为示例。这种集合类的实现具有简单高效的特点,CollectionBase已经实现了较为完善的功能,实施者只要在其基础上扩展自己所需的功能即可。
索引器的简单实现
我们惯于操作数组的形式通常为array[i],集合类可以看作是“对象的数组”,在C#中,帮助集合类实现数组式索引功能的就是索引器:
public Customer this[int index]
{
get
{
return (Customer) List[index];
}
}
将以上代码加入到Customers类后,就实现了以整形index为参数,以List[index]强制类型转换后的Customer类型返回值的Customers类只读索引器,使用者以Customers[i].Name的方式,就可以访问Customers集合中第i个Customer对象的姓名字段,是不是很神奇呢?文中的索引器代码并未考虑下标越界的问题,越界的处理方式应参照与之类似的Remove方法。作者在此只实现了索引器的get访问,没有实现set访问的原因将在下文中讨论。
Item的两种实现方式
用过VB的朋友们一定都很熟悉Customers.Itme(i).Name的形式,它实现了与索引器相同的作用,即通过一个索引值来访问集合体中的特定对象,但Item在C#当中应该以怎样的形式实现呢?首先想到的实现途径应该是属性,但你很快就会发现C#的属性是不支持参数的,所以无法把索引值作为参数传入,折中的办法就是以方法来实现:
public Customer Item (int Index)
{
return (Customer) List[Index];
}
这个Item方法已经可以工作了,但为什么说是折中的办法呢,因为对Item的访问将是采用Customers.Item(i).Name的语法形式,与C#‘[]’作数组下标的风格不统一,显的有些突兀,但如果希望在语法上做到统一,哪怕是性能受一些影响也无所谓的话有没有解决之道呢?请看以下代码:
public Customers Item
{
get
{
return this;
}
}
这是以属性形式实现的Item接口,但是由于C#的属性不支持参数,所以我们返回Customers对象本身,也就是在调用Customers对象Item属性时会引发对Customers索引器的调用,性能有所下降,但是的确实现了Customers.Item[i].Name的语法风格统一。对比这两种Item的实现,不难得出结论:以不带参数的属性形式实现的Item依赖于类的索引器,如果该类没有实现索引器,该属性将无法使用;并且由于对Item的访问重定向到索引器性能也会下降;唯一的理由是:统一的C#索引下标访问风格;采用方法实现的裨益正好与之相反,除了语法风格较为别扭外,不存在依赖索引器、性能下降的问题。鱼与熊掌难以兼得,如何取舍应依据开发的实际需求决定。
中间语言的编译缺省与Attribute的应用
如果你既实现了标准的索引器,又想提供名为“Item”的接口,编译时就会出现错误“类‘WindowsApplication1.Customers’已经包含了“Item”的定义”,但除了建立索引器外,你什么也没有做,问题到底出在哪里?我们不得不从.NET中间语言IL来寻找答案了,在.NET命令行环境或Visual Studio .NET 命令提示环境下,输入ILDASM,运行.NET Framework MSIL 反汇编工具,通过主菜单中的‘打开’加载只有索引器没有Item接口实现的可以编译通过的.NET PE执行文件,通过直观的树状结构图找到Customers类,你将意外地发现C#的索引器被解释成了一个名为Item的属性,以下是IL反编译后的被定义为Item属性的索引器代码:
.property instance class WindowsApplication1.Customer
Item(int32)
{
.get instance class WindowsApplication1.Customer WindowsApplication1.Customers::get_Item(int32)
} // end of property Customers::Item
问题总算水落石出,就是C#编译器‘自作聪明’地把索引器解释成了一个名为Item的属性,与我们期望实现的Item接口正好重名,所以出现上述的编译错误也就在所难免。那么,我们有没有方法告知编译器,不要将索引器命名为缺省Item呢?答案是肯定的。
解决方法就是在索引器实现之前声明特性:
[System.Runtime.CompilerServices.IndexerName("item")]
定义这个IndexerName特性将告知CSharp编译器将索引器编译成item而不是默认的Item ,修改之后的索引器IL反汇编代码为:
.property instance class WindowsApplication1.Customer
item(int32)
{
.get instance class WindowsApplication1.Customer WindowsApplication1.Customers::get_item(int32)
} // end of property Customers::item
当然你可以将索引器的生成属性名定义成其它名称而不仅限于item,只要不是IL语言的保留关键字就可以。经过了给索引器命名,你就可以自由地加入名为“Item”的接口实现了。
以下为Customer类和Customers类的调试代码,在作者的Customers类中,为说明问题,同时建立了以item为特性名的索引器、一个Items方法和一个Item属性来实现对集合元素的三种不同访问方式,实际的项目开发中,一个类的索引功能不需要重复实现多次,可能只实现索引器或一个索引器加上一种形式的Item就足够了:
public class CallTest
{
public static void Main()
{
Customers custs=new Customers();
System.Console.WriteLine(custs.Count.ToString());//Count属性测试
Customer aCust=new Customer();//将调用不带参数的构造函数
aCust.Name ="Peter";
custs.Add(aCust);//Add方法测试
System.Console.WriteLine(custs.Count.ToString());
System.Console.WriteLine(custs.Item[0].Name);//调用Item属性得到
custs.Items(0).Name+="Hu";//调用Items方法得到
System.Console.WriteLine(custs[0].Name);//调用索引器得到
custs.Add(new Customer("Linnet"));//将调用带name参数的构造函数
System.Console.WriteLine(custs.Count.ToString());
System.Console.WriteLine(custs.Items(1).Name);//调用Items方法得到
custs.Item[1].Name+="Li";//调用Items方法得到
System.Console.WriteLine(custs[1].Name);//调用索引器得到
custs.Remove(0);//Remove方法测试
System.Console.WriteLine(custs.Count.ToString());
System.Console.WriteLine(custs[0].Name);//Remove有效性验证
custs[0].Name="Test passed" ;//调用索引器得到
System.Console.WriteLine(custs.Item[0].Name);
custs.Clear();
System.Console.WriteLine(custs.Count.ToString());//Clear有效性验证
}
}
输出结果为:
0
Initialize instance without parameter
1
Peter
PeterHu
Initialize instance with parameter
2
Linnet
LinnetLi
1
LinnetLi
Test passed
0
2.采用内建ArrayList对象的方式实现集合类:
或许有经验的程序员们早已经想到,可以在一个类中内建一个数组对象,并在该类中通过封装对该对象的访问,一样能够实现集合类。以下是采用这种思路的Contact元素类和Contacts集合类的实现框架:
public class Contact
{
protected string summary;
/// <summary>
/// 客户联系说明
/// </summary>
public string Summary
{
get
{
System.Console.WriteLine("getter access");
return summary;//do something, as get data from data source
}
set
{
System.Console.WriteLine("setter access");
summary=value;// do something , as check validity or Storage
}
}
public Contact()
{
}
}
public class Contacts
{
protected ArrayList List;
public void Add(Contact contact)
{
List.Add(contact);
}
public void Remove(int index)
{
if (index > List.Count - 1 || index < 0)
{
System.Console.WriteLine("Index not valid!");
}
else
{
List.RemoveAt(index);
}
}
public int Count
{
get
{
return List.Count;
}
}
public Contact this[int index]
{
get
{
System.Console.WriteLine("indexer getter access");
return (Contact) List[index];
}
set
{
List[index]=value;
System.Console.WriteLine("indexer setter access ");
}
}
public Contacts()
{
List=new ArrayList();
}
}
通过这两个类的实现,我们可以总结以下要点:
采用ArrayList的原因
在Contacts实现内置集合对象时,使用了ArrayList类,而没有使用大家较为熟悉的Array类,主要的原因有:在现有的.NET v1.1环境中,Array虽然已经暴露了IList.Add、IList.Insert、IList.Remove、IList.RemoveAt等典型的集合类接口,而实际上实现这些接口总是会引发 NotSupportedException异常,Microsoft是否在未来版本中实现不得而知,但目前版本的.NET显然还不支持动态数组,在MS推荐的更改Array大小的办法是,将旧数组通过拷贝复制到期望尺寸的新数组后,删除旧数组,这显示是费时费力地在绕弯路,无法满足集合类随时添加删除元素的需求;ArrayList已经实现了Add、Clear、Count、IndexOf、Insert、Remove、RemoveAt等集合类的关键接口,并且有支持只读集合的能力,在上边的Contacts类中,只通过极少的封装代码,就轻松地实现了集合类。另一个问题是我们为什么不采用与Customers类似的从System.Collections.ArrayList继承的方式实现集合类呢?主要是由于将ArrayList对象直接暴露于类的使用者,将导致非法的赋值,如用户调用arraylist.Add方法,无论输入的参数类型是否为Contact,方法都将被成功执行,类无法控制和检查输入对象的类型与期望的一致,有悖该类只接纳Contact类型对象的初衷,也留下了极大的安全隐患;并且在Contact对象获取时,如不经过强制类型转换,Contacts元素也无法直接以Contact类型形式来使用。
集合类中的Set
在集合类的实现过程中,无论是使用索引器还是与索引器相同功能的“Item”属性,无可避免地会考虑是只实现getter形成只读索引器,还是同时实现getter和setter形成完整的索引器访问。在上文的示例类Customers中就没有实现索引器的setter,形成了只读索引器,但在Customer类和Customers类的调试代码,作者使用了容易令人迷惑的“custs[0].Name="Test passed"”的访问形式,事实上,以上这句并不会进入到Customers索引器的setter而是会先执行Customers索引器的getter得到一个Customer对象,然后设置这个Customer的Name字段(如果Name元素为属性的话,将访问Customer类Name属性的setter)。那么在什么情况下索引器的setter才会被用到呢?其实只有需要在运行时动态地覆盖整个元素类时,集合类的setter才变得有意义,如“custs [i]=new Customer ()”把一个全新的Customer对象赋值给custs集合类的已经存在的一个元素,这样的访问形式将导致Customers的setter被访问,即元素对象本身进行了重新分配,而不仅仅是修改现有对象的一些属性。也就是说,由于Customers类没有实现索引器的setter 所以Customers类对外不提供“覆盖”客户集合中既有客户的方法。与此形成鲜明对照的是Contacts类的索引器既提供对集合元素的getter,又提供对集合元素的setter,也就是说Contacts类允许使用者动态地更新Contact元素。通过对Contacts和Contact两个类运行以下测试可以很明确说明这个问题:
public class CallTest
{
public static void Main()
{
Contacts cons=new Contacts();
cons.Add(new Contact());
cons[0]=new Contact();//trigger indexer setter
cons[0].Summary="mail contact about ticket";
System.Console.WriteLine(cons[0].Summary);
}
}
理所当然的输出结果为:
indexer setter access
indexer getter access
setter access
indexer getter access
getter access
mail contact about ticket
明确认识到了索引器setter的作用后,在类的实现中就应当综合实际业务特点、存取权限控制和安全性决定是否为索引器建立setter机制。
属性-强大灵活的字段 合二为一的方法
在最初实现Customer类时,我们使用了一个公共字段Name,用作存取客户的姓名信息,虽然可以正常的工作,但我们却缺乏对Name字段的控制能力,无论类的使用者是否使用了合法有效的字段赋值,字段的值都将被修改;并且没有很好的机制,在值改变时进行实时的同步处理(如数据存储,通知相关元素等);另外,字段的初始化也只能放在类的构造函数中完成,即使在整个对象生命周期内Name字段都从未被访问过。对比我们在Contact类中实现的Summary属性,不难发现,属性所具有的优点:属性可以在get时再进行初始化,如果属性涉及网络、数据库、内存和线程等资源占用的方式,推迟初始化的时间,将起到一定的优化作用;经过属性的封装,真正的客户联系说明summary被很好地保护了起来,在set时,可以经过有效性验证再进行赋值操作;并且在getter和setter前后,可以进行数据存取等相关操作,这一点用字段是不可能实现的。所以我们可以得出结论,在字段不能满足需求的环境中,属性是更加强大灵活的替代方式。
另外,属性整合了“get”和“set”两个“方法”,而采用统一自然的接口名称,较之JAVA语言的object.getAnything和object.setAnything语法风格更加亲和(事实上,C#中的属性只不过是对方法的再次包装,具有getter和setter的Anything属性在.NET IL中,依然会被分解成一个由Anything属性调用的get_Anything和set_Anything两个方法)。
集合类内联的方式
在文章最初的Customer类中使用了公共字段public Contacts Contacts=new Contacts()实现了customer. Contacts[]形式的集合类内联接口,这是一种最为简单但缺乏安全性保护的集合类集成方式,正如以上所述属性的一些优点,采用属性形式暴露一个公共的集合类接口,在实际存取访问时,再对受封状保护的集合类进行操作才是更为妥当完善的解决方案,如可以把Customer类内联的集合Contacts的接口声明改为:
protected Contacts cons; //用于类内封装的真正Contacts对象
public Contacts Contacts//暴露在类外部的Contacts属性
{
get
{
if (cons == null) cons=new Contacts();
return cons;
}
set
{
cons=value;
}
}
最终,customers[i].Contacts[x].Summary的形式就被成功地实现了。
实例化的最佳时机
.NET的类型系统是完全对象化的,所有的类型都是从System.Object派生而来,根据类型的各自特点,可以分为值类型和引用类型两大阵营。值类型包括结构(简单的数值型和布尔型也包括在内)和枚举,引用类型则包括了类、数组、委托、接口、指针等,对象化的一个特点是直到对象实例化时才为对象分配系统资源,也就是说灵活适时地实例化对象,对系统资源的优化分配将产生积极意义。在一些文章中所建议的“Lazy initialization”倡导在必要时才进行对象的实例化,本着这样的原则,从类的外部来看,类可以在即将被使用时再进行初始化;在类的内部,如属性之类的元素,也可以不在构造函数中初始化,而直到属性的getter被真正访问时才进行,如果属性一直没有被读取过,就不必要无意义地占用网络、数据库、内存和线程等资源了。但是也并不是初始化越晚越好,因为初始化是需要时间的,在使用前才进行初始化可能导致类的响应速度过慢,无法适应使用者的实时需求。所以在资源占用和初始化耗时之间寻求一个平衡点,才是实例化的最佳时机。
总结
本文围绕实现集合类的两种途径-从CollectionBase继承实现和内建ArrayList对象实现,为大家展示了部分集合、索引器、属性、特性的应用以及.NET环境中的类构造函数、对象优化、类关联等其它相关知识。通过本文浅显的示例和阐述,希望可以启发读者的灵感,推出更加精辟合理的基础理论和应用模型。
分享到:
相关推荐
《永磁无刷直流电机控制系统与软件综合研究——集成电机计算软件、电机控制器及电磁设计软件的创新设计与实践》,永磁无刷直流电机计算与控制软件:高效电机控制器与电磁设计工具,永磁无刷直流电机计算软件,电机控制器,无刷电机设计软件,电机电磁设计软件 ,永磁无刷直流电机计算软件; 电机控制器; 无刷电机设计软件; 电机电磁设计软件,无刷电机设计专家:永磁无刷直流电机计算与控制器设计软件
新能源汽车VCU开发模型及策略详解:从控制策略到软件设计全面解析,新能源汽车VCU开发模型及策略详解:从控制策略到软件设计全面解析,新能源汽车VCU开发模型及控制策略,MBD电控开发 新能源汽车大势所向,紧缺VCU电控开发工程师,特别是涉及新能源三电系统,工资仅仅低于无人驾驶、智能驾驶岗位。 ——含控制策略模型 整车控制策略详细文档 通讯协议文档 接口定义 软件设计说明文档 等(超详细,看懂VCU电控策略开发就通了) 内容如下: 新能源汽车整车控制器VCU学习模型,适用于初学者。 1、模型包含高压上下电,行驶模式管理,能量回馈,充电模式管理,附件管理,远程控制,诊断辅助功能。 2、软件说明书(控制策略说明书) 3、模型有部分中文注释 对想着手或刚开始学习整车控制器自动代码生成或刚接触整车控制器有很大帮助。 ,新能源汽车VCU开发模型; 控制策略; MBD电控开发; 模型学习; 代码生成; 整车控制器; 能量回馈; 诊断辅助功能,新能源汽车电控开发详解:VCU控制策略模型及学习手册
内容概要:本文详细介绍了两种利用 Python 读取 Excel 文件的不同方法,分别是基于 pandas 和 openpyxl。对于想要利用Python 处理 Excel 数据的读者来说,文中不仅提供了简洁明了的具体代码片段以及执行效果展示,还针对每个库的应用特性进行了深度解析。此外,文档提到了一些进阶应用技巧如只读特定的工作薄、过滤某些列等,同时强调了需要注意的地方(像是路径设置、engine 参数调整之类),让读者可以在面对实际项目需求时做出更加明智的选择和技术选型。 适合人群:对 Python 有基本掌握并希望提升数据读取能力的开发人员。 使用场景及目标:适用于任何涉及到批量数据导入或是与 Excel 进行交互的业务流程。无论是做初步的数据探索还是深入挖掘隐藏于电子表格背后的故事,亦或是仅为了简化日常办公自动化任务都可以从中受益。最终目标帮助使用者熟悉两大主流 Excel 解决方案的技术特性和最佳实践。 阅读建议:本文既是一份详尽的学习指南也是一份方便随时查阅的手册。因此初学者应当认真研究所提供的示例,而有一定经验者也可以快速定位到感兴趣的部分查看关键要点。
# 医护人员排班系统 ## 1. 项目介绍 本系统是一个基于SpringBoot框架开发的医护人员排班管理系统,用于医院管理医护人员的排班、调班等工作。系统提供了完整的排班管理功能,包括科室管理、人员管理、排班规则配置、自动排班等功能。 ## 2. 系统功能模块 ### 2.1 基础信息管理 - 科室信息管理:维护医院各科室基本信息 - 医护人员管理:管理医生、护士等医护人员信息 - 排班类型管理:配置不同的排班类型(如:早班、中班、晚班等) ### 2.2 排班管理 - 排班规则配置:设置各科室排班规则 - 自动排班:根据规则自动生成排班计划 - 排班调整:手动调整排班计划 - 排班查询:查看各科室排班情况 ### 2.3 系统管理 - 用户管理:管理系统用户 - 角色权限:配置不同角色的操作权限 - 系统设置:管理系统基础配置 ## 3. 技术架构 ### 3.1 开发环境 - JDK 1.8 - Maven 3.6 - MySQL 5.7 - SpringBoot 2.2.2 ### 3.2 技术栈 - 后端框架:SpringBoot - 持久层:MyBatis-Plus - 数据库:MySQL - 前端框架:Vue.js - 权限管理:Spring Security ## 4. 数据库设计 主要数据表: - 科室信息表(keshixinxi) - 医护人员表(yihurengyuan) - 排班类型表(paibanleixing) - 排班信息表(paibanxinxi) - 用户表(user) ## 5. 部署说明 ### 5.1 环境要求 - JDK 1.8+ - MySQL 5.7+ - Maven 3.6+ ### 5.2 部署步骤 1. 创建数据库并导入SQL脚本 2. 修改application.yml中的数据库配置 3. 执行maven打包命令:mvn clean package 4. 运行jar包:java -jar xxx.jar ## 6. 使用说明 ### 6.1 系统登录 - 管理员账号:admin - 初始密码:admin ### 6.2 基本操作流程 1. 维护基础信息(科室、人员等) 2. 配置排班规则 3. 生成排班计划 4. 查看和调整排班 ## 7. 注意事项 1. 首次使用请及时修改管理员密码 2. 定期备份数据库 3. 建议定期检查和优化排班规则
MATLAB仿真的夫琅禾费衍射强度图:圆孔、圆环、矩形孔定制研究,MATLAB仿真:夫琅禾费衍射强度图的可定制性——以圆孔、圆环及矩形孔为例的研究分析,MATLAB夫琅禾费衍射强度图仿真 圆孔,圆环,矩形孔可定制。 ,MATLAB; 夫琅禾费衍射; 强度图仿真; 圆孔; 圆环; 矩形孔; 可定制。,MATLAB仿真夫琅禾费衍射强度图:定制孔型(圆孔/圆环/矩形)
详细介绍及样例数据:https://blog.csdn.net/samLi0620/article/details/145652300
基于Dugoff轮胎模型与B08_01基础建模的七自由度车辆动力学模型验证:利用MATLAB 2018及以上版本与CarSim 2020.0软件的仿真对比研究,基于Dugoff轮胎模型与B08_01框架的七自由度车辆动力学模型验证——使用MATLAB 2018及以上版本与CarSim 2020.0软件进行仿真对比研究,七自由度车辆动力学模型验证(Dugoff轮胎模型,B08_01基础上建模) 1.软件: MATLAB 2018以上;CarSim 2020.0 2.介绍: 基于Dugoff轮胎模型和车身动力学公式,搭建7DOF车辆动力学Simulink模型,对相关变量(质心侧偏角,横摆角速度,纵、横向速度及加速度)进行CarSim对比验证。 ,核心关键词:七自由度车辆动力学模型验证; Dugoff轮胎模型; B08_01建模基础; MATLAB 2018以上; CarSim 2020.0; Simulink模型; 变量对比验证。,基于Dugoff轮胎模型的七自由度车辆动力学模型验证与CarSim对比
【毕业设计】基于Java+servlet+jsp+css+js+mysql实现“转赚”二手交易平台_pgj
微猫恋爱聊妹术小程序源码介绍: 微猫恋爱聊妹术小程序源码是一款全新升级的聊天工具,它采用全新主题和UI,完美支持分享朋友圈功能。同时,它的独立后台也进行了大规模更新,让操作更加简单。其中,课堂页面、搜索页面和子话术列表页面等,均增加了流量主展示,具有超多的功能。 安装教程: 您可以先加入微猫恋爱聊妹术小程序源码的赞助群,然后在群内找到魔方安装说明。根据源码编号找到相应的安装说明,非常详细,让您轻松完成安装。
电气安装工程安全技术规程_蒋凯,杨华甫,马仲范,王清禄译;孙照森校;鞍钢工程技术编委会编
基于Copula函数的风光空间相关性联合场景生成与K-means聚类削减MATLAB研究,基于Copula函数的风光空间相关性联合场景生成与K-means聚类削减算法研究,基于copula的风光联合场景生成?K-means聚类并削减 MATLAB 由于目前大多数研究的是不计风光出力之间的相关性影响,但是地理位置相近的风电机组和光伏机组具有极大的相关性。 因此,采用 Copula 函数作为风电、光伏联合概率分布,生成风、光考虑空间相关性联合出力场景,在此基础上,基于Kmeans算法,分别对风光场景进行聚类,从而实现大规模场景的削减,削减到5个场景,最后得出每个场景的概率与每个对应场景相乘求和得到不确定性出力 ,基于Copula的风光联合场景生成; K-means聚类削减; 空间相关性; 概率分布; 场景削减,基于Copula与K-means的风光联合场景生成与削减研究
模块化多电平变流器MMC的VSG控制技术研究:基于MATLAB-Simulink的仿真分析与定制实现——支持三相与任意电平数,构网型模块化多电平变流器MMC的VSG控制策略与仿真模型:三相负荷变动下的虚拟同步发电机控制研究,构网型 模块化多电平变流器 MMC 的VSG控制 同步发电机控制 MATLAB–Simulink仿真模型,可按需求定制 10电平.14电平,任意电平可做。 三相MMC,采用VSG控制。 设置负荷变动,调整有功无功,保持电网电压和频率 ,构网型模块化多电平变流器; MMC的VSG控制; 虚拟同步发电机控制; MATLAB–Simulink仿真模型; 任意电平可做; 三相MMC; 负荷变动; 有功无功调整; 电网电压和频率保持。,基于VSG控制的模块化多电平变流器(MMC)的构网型仿真模型
暗通道算法DCP-Python实现
南师大实验室安全准入知识供学习
纯openMV寻迹小车.zip
【毕业设计】基于Java mvc架构开发的完整购物网站
以下是针对初学者的 **51单片机入门教程**,内容涵盖基础概念、开发环境搭建、编程实践及常见应用示例,帮助你快速上手。
springboot医院信管系统--
springboot私人健身与教练预约管理系统--
yolov8-0的资源