泛型(generic)是C# 2.0推出的新语法,它专门用于处理多段代码在不同的数据类型上执行相同的指令的情况而设计的。比如说编程时,功能非常相似的模块,只是它们所处理的数据类型不同,但是我们却需要写不同的方法来实现它,很明显,这加大了我们的工作量,也很乏味。有什么办法能够解决这个问题呢?它就是泛型了,可以让多个类型共享一组代码。通过压栈例子可以更清晰的了解泛型
class IntStack {int[] arr;公共无效推(int x){...}; //将int类型的值压栈 } class FloStack {float[] arr; public void push(float x) { ...};//将float类型的值压栈 }
这两类功能一样,只是操作的数据类型不同,并且如果需要新类型(double、string)等时,又需要进行重复的操作,下面介绍怎么通过类型泛化解决这个问题
class MyStack{ T[] arr;公共无效推(T x){...}; }
创建泛型类时,先在类名后面添加
<泛型类型不是类型,而是类型的模板,只要比类型不是对象对象的模板一样。< /p>
C#提供了五种泛型:类、结构、接口、委托和方法。前面四种是类型,而方法是成员。还是很迷吧,接下来对五种泛型分别进行讲解。
1、泛型类型类
由于泛型类不是实际的类,而是类的模板,所以我们必须先从它们构造出实际的类类型,然后创建这个构造后的类类型的实例。
从泛型类型创建实例的过程是:a:声明泛型类型 b:通过提供真实类型创建构造类型 创建 c: 从构造类型实例<<
> ="img-center">
使用 System;使用 System.Collections.Generic;使用 System.Linq;使用 System.Text;使用 System.Threading.Tasks;命名空间泛型{ class Test//构造泛型类,T1,T2为类型参数 { public T1 var1;公共 T2 var2; public void print() { Console.WriteLine("var1:{0} var2:{1}", var1 , var2); } } class Program { static void Main(string[] args) { // 两种实例化的方式,效果相同 Test first = new Test ();//int和string为类型实参,分别对应T1,T2 var secondary = new Test ();首先.var1 = 123; //第一个中,var1只能为int类型,当然也可以通过强制类型转换成其他的类型。var2 = "Good Luck!"; // 只能为string类型,同上first.print();第二个.var1 =“你好世界”;第二个.var2 = 345;第二个.print(); Console.ReadKey(); } }}
同一个泛型可以构建出很多不同的类型,互不干扰,每个都有独立的类类型,就需要有独立的非泛型类声明一样
<
<2、泛型结构
struct MyStruct{ public T val;}
和泛型类类似,不过多叙述
<3、泛型委托
泛型委托和非泛型委托非常相似,不过类型参数决定了它能接受什么方法
delegate S MyDel(T value) ;//S为委托返回类型,T为委托参数类型
<4、泛型接口
使用 System;使用 System.Collections.Generic;使用 System.Linq;使用 System.Text;使用 System.Threading.Tasks;命名空间泛型{ 接口 Test{ void Print(T value); } } /* * 泛型的接口实现必须唯一,必须保证类型参数组合不会再类型中产生两个重复的接口* class Simple : Test, Test 是错误的,因为S有可能是int类型 * 你也可以再非泛型类型中实现类型泛接口 * class Simple : Test, Test */ class Simple : Test{ public void Print(S value) { Console.WriteLine("值为 {0}",value); } } } class Program { static void Main(string[] args) { var IntSimp = new Simple(); var StrSimp = new Simple<字符串>(); IntSimp.Print(123); StrSimp.Print("你好世界"); Console.ReadKey(); } }}
注意:泛型接口的名称和非泛型接口不会冲突,我们可以在前面的代码中声明一个测试的非泛型接口,程序能正常执行
<5、泛型方法
<泛型方法有两个参数列表,封闭在圆括号内的方法参数列表和在尖括号内所示的类型参数列表,如下<
public void Print(S val1, T val2){ ...}
顾名思义,即对类型参数进行约束,让编译器知道参数可以接受哪些类型,只有符合约束的类型才能替代给定的类型参数,来产生构造类型
对此,可以使用where子句,其语法为: where TypeParam :constraint,constraint ...
一个where对应一个类型参数,如果类型参数有多个约束,则用分隔符
class Testwhere T : IComparable where S : new() {...} //不理解没关系,下面会讲 //注意两个地方之间没有任何符号分隔,可以以任意顺序排列
,有五种约束类型
其中子句的约束必须有特定的顺序
a:最多只能有一个主约束,如果有则必须放在上层,主约束可以为结构、类别或基类名
b:可以有任意多个的接口名约束,其类型为接口名称
:如果存在构造函数约束,则必须放在最后面,构造函数约束即为 new()
“协变”是指能够使用与原始指定的派生类型相比,派生程度更大的类型。
“预警”则指能够使用派生程度更小的类型。
可以显式使用关键字指定类型参数的协变,用在关键字指定类型参数的波形中
接口 IItf{...}delegate void Test (T a);
使用in和out关键字的显着变化方式只适合于委托和接口,不适用于类、结构和方法
/******************************************** ******C#根据泛型指定方法的返回值类型
在编写程序的时候经常会查询同一个方法有多次重载,其生成参数相似,只是类型不一致。如下所示:
public double GetData(){ }
public string GetData(){ }
public int GetData() { }
对方法进行重载那么宽度程序封装肿胀,有没有更好的方法解决呢?
这里我们采用泛型解决方案,具体代码实现如下:
public static T GetData
{
类型 t = typeof(T);
if(t == typeof(Double))
{
double result = 1.00009;
return (T)(Object)result;
}
else if(t == typeof(string))< br /> {
string result = "这是字符串类型";
return (T)(Object)result;
}
return default( T);
}
这是测试方法和测试结果
public static void Main(string[]args)
{
string result1 = GetData
Console.WriteLine(result1); // 这是字符串类型
double result2 = GetData
Console.WriteLine(result2); //1.00009
Console.ReadKey();
}
当然这个方法除了解决实体参数作为泛返回型的问题外,还有另外的用处是:在调用方法时指定返回值类型,例如Newtonsoft.Json中json对象的反序列化就是这样用的。
List>(jsonStr);
/******************************************** ************c#带返回值的action_C#委托和匿名函数大总结
一、匿名函数
匿名函数是一个“内联”语句或表达式,可以在需要委托类型的任何地方使用。可以使用匿名函数来初始化命名委托,或传递命名委托(而不是命名委托类型)作为方法参数。
C#中有两种匿名函数:Lambda表达式和匿名方法。
二、C#中委托的发展历程
C# 1.0中,您可以通过在使用代码中位置定义的方法显式初始化委托来创建委托的实例。
C# 2.0 引入了其他匿名方法的概念,作为一种编写的可在委托调用中执行的未命名内联语句
C# 3.0 引入了 Lambda 表达式,这种表达式与匿名方法的概念类似,但增加了表达力并且更简单练。这两个功能系统称为“匿名函数”。通常,针对 .NET Framework 版本 3.5 及更高版本的应用程序应使用 Lambda 表达式。
测试代码如下:
public class HistoryDelegate
{
private delegate void Delegate1(string str_);
private void OnDelegate1(string str1_)
{
Console.WriteLine($" OnDelegate1: {str1_}");
}
public void OnTest()
{
//C# 1.0
Delegate1 d1 = new Delegate1(OnDelegate1);
d1("d1");
//C# 2.0
Delegate1 d2 = delegate (string str) { Console.WriteLine($"{str} ”); };
d2("d2");
//C# 3.0
Delegate1 d3 = (x) => { Console.WriteLine($"{x}"); };
d3("d3");
}
}
三、C#内置泛型委托
Action委托:
Action是无返回值的泛型委托。
Action表示无参,无返回值的委托
Action
Action
Action
Action至少0个参数,至多16个参数,无返回值。
测试代码如下:
public class TestAction
{
private void OnAction1(string str_)
{
Console.WriteLine($"OnAction1:{str_}");
}
private void OnAction2(int index_ , string str_) => Console.WriteLine($"OnAction1:{index_}/{str_}");
public void OnTest()
{< br /> Action
action1("action1");
Action
action2(2, "action2");
}
}
Func委托
Func是有返回值的泛型委托,<>中,最后一个类型为返回值类型。
Func
Func
Func
Func
Func至少0个参数,至多16个参数,根据泛型返回值返回。必须有返回值,不可void
测试代码如下:
public class TestFunc
{
private string OnFunc1( int index_, string str_)
{
Console.WriteLine($"OnFunc1:{index_}/{str_}");
return str_ ;
}
private int OnFunc2(string str1, string str2_)
{
Console.WriteLine($"OnFunc2:{str1}/{str2_ }");
return 1;
}
私有字符串 TestFunc1
{
return func_(index_, str_);
}
public void OnTest()
{
Console.WriteLine(TestFunc1
Func
Console.WriteLine(func2("name1", "name2"));< br /> }
}
Predicate委托
Predicate是返回bool型的泛型委托
Predicate
Predicate有且只有一个参数,返回值固定为bool
测试代码如下:
public class TestPredicate
{
private bool OnPredicate1(int index_)
{
if (index_ > 0)
{< br /> return true;
}
else
{
return false;
}
}
private bool TestPredicate1
{
return predicate1(index_);
}
public void OnTest()
{
Console.WriteLine(TestPredicate1(OnPredicate1) , 0));
}
}
总结:
委托类似于C++函数指针,但它们是类型安全的。
委托允许将方法作为参数进行提交。
委托可用于定义回调方法。
委托可以链接在一起;例如,可以对一个事件调用多个方法。
方法不一定与委托签名完全匹配。
委托至少0个参数,至多32个参数,可以无返回值,也可以返回指定值类型
Func可以接受0个至16个格式化参数,必须有返回值
Action可以接受0至16个格式化参数,无返回值
Predicate只能接受一个格式化参数,返回值为bool类型
四、优化技巧
1.问题避免函数引用外部变量,使其可被静态化
2.搞清楚哪些变量被匿名函数引用了,防止内存泄漏
3.尽量把被引用的变量声明放在后面,用变量复制来延迟匿名函数创建
//错误的写法
void Error1()
{
var printList = new List
for ( int i = 0; i < 10; ++i ) {
// 内部循环一直在构造匿名函数对象,分配大量的内存
printList.Add( () => Debug.Log( i ) );
}
for ( int j = 0; j < printList.Count; ++ j ) {
printList[ j ](); // 结果总是输出10
}
}
void Error2()
{
var list = new List
list.Add( 1 );
list.Add( 2 );
int id = 0;
if ( id == 5 )
{
// 假设遇到问题
// 表面上看,匿名函数对象在构造这里
// 但实际上,匿名对象在id声明处就已经提前构造好了
//这样会100%造成内存分配
list.Find( value => value == id );
}
}
//正确的写法
void Error1()
{
var printList = new List
for ( int i = 0; i < 10; ++i )
{
// 为了避免循环变量被引用
// 复制i到局部变量,让其被匿名函数引用
var _i = i ;
printList.Add( () => Debug.Log( _i ) );
}
// 结果虽然是对的,但实际上编码中,还是要避免循环中构造匿名函数
// ...
}
void Error2()
{
var list = new List
// ...
int id = 0;
if ( id == 5 )
{
//同理,这样匿名函数构造的位置操作延迟到了表达式内部
// 消除大部分时候的内存分配
var _id = id;
list.Find( value => value == _id );
}
}
五、常用技巧写法
public class TestProperty
{
//定义接口属性
public string Name => "Hello";
private string m_name1 = "World";
public string Name1 => m_name1;
// 给定义属性默认值
public int Age { get;放; } = 18;
private int m_age1 = 18;
public int Age1
{
get { return m_age1; }
set { m_age1 = value; }
}
private Predicate
public void OnTest()
{
// 匿名函数的简洁写法:可以省掉,参数列表括号,函数体括号,返回语句
List
string result2 = list.Find((x) => { return x == "ccc"; });< br /> if (null == result2)
{
Console.WriteLine("未找到 ccc");
}
if (null = = onRule)
{
onRule =OnRule;
string reslut3 = list.Find(onRule);
}
}
private bool OnRule(string str_)
{
if (str_ == "aaa")
{
Console.WriteLine("OnRule查找aaa");
return true;
}
else
{
Console.WriteLine("OnRule 找不到 aaa");
返回 false;< br /> }
}
}
/******************************** ********
1、为什么要使用委托
将一个方法作为参数传递给另一个方法
2、委托概念
声明:
public delegate int DelegateName(int a, int b);
1
声明一个委托类型,可以用访问修饰符修饰,委托关键字,有返回值和参数
委托所指向的函数必须跟委托相同的签名,即相同的参数个数,相同的参数类型和相同的返回值类型
创建委托实例写法:
委托委托名=new委托(会调用的方法名)
或者:
委托委托名=会调用的方法名
3、泛型委托
声明:
public delegate T委托名
1
我们每次要使用一个委托时,都需要先这个声明委托类,规定参数和返回值类型,然后才能实例化、调用。为了简化这个过程,.NET框架为我们封装了三个泛型委托类,它们分别是Action,Func和Predicate。因此大部分情况下我们不需要再声明委托,可以拿来直接实例化使用,方便了我们的日常编码。
1). Action
public delegate void Action
1
Action是无返回值的泛型委托
Action 表示无参,无返回值的委托
Action
Action
Action Action 至少 0 个参数,至多 16 个参数,无返回值。 创建委托实例写法: 2). Func public delegate 函数 Func Func Func至少0个参数,至多16个参数,根据返回值泛型返回。必须有返回值,不可void 创建委托实例写法: 3). Predicate public delegate bool Predicate Predicate 是返回bool型的泛型委托 Predicate Predicate有且只有一个参数,返回值固定为bool 创建委托实例写法: 4、匿名函数(没有函数名,直接方法体) 例子如下: static void Main(string[] args) } 5、 Lambda 表达式 好处:Lambda简化了匿名委托的使用,减少了开发中需要编写的代码量。 具体内容:它可以包含表达式和语句,并且可用于创建委托或表达式目录树类型,支持带可绑定到委托或表达式树的输入参数的内联表达式。 写法:所有Lambda表达式都使用Lambda表达式=>,该表达式读作“goes to”。Lambda表达式的左边是输入参数(如果有),右边是表达式或语句块。Lambda表达式x => x * x读作“x gets to x times x” 。 创建委托实例写法: 六、调用委托实例通用写法: 2.委托实例(参数列表) 3.this.Invoke(委托实例, new object[] { 参数列表}); 封装一个两个参数并返回TResult参数指定的类型值的方法。 语法 备注< /p> 使用此委托表示一种可以以参数形式提交的方法,而不用显式的自定义委托封装的方法必须与此委托定义的方法签名相对应。方法必须具有两个均通过值传递给它的参数,并且必须返回值。 在使用 Func 示例 下面的示例演示如何声明和使用 Func 示例 了解完这些以后,我们来看看它 那么怎么办呢?我们可以利用Func 数据访问层 表示层调取 /**************************************** ** 此委托的定义如下: (1)in T :此委托封装方法的参数类型。 (2)out TResult :此委托封装方法的返回值类型。 可以使用此委托表示一种能以参数的形式传递的方法,而不是用显式声明自定义委托。封装的方法必须与此委托定义的方法签名相对应。既然如此,封装的方法必须有一个通过值传递给它的参数,并且必须返回值。 查看代码 查看代码 当然,我们还可以借助匿名方法更便捷地使用: 查看代码 透明可以可以看出,现在使用Func委托时,不一定显式定义一个新的委托抽取抽取方法分配给该委托。 我们已经知道Func委托是带返回指定值类型的委托,那么我们来看看在实际开发场景的一个场景。还是以刚才那个数据集合PersonList为例,很多时候我们需要对从数据库中读取的数据集合进行二次筛选,接下来我们可以使用List集合的Select方法,我们将一个Func委托实例作为方法参数传递给Select方法,就可以返回一个符合我们指定条件的新数据集合看看 <(1)先来Select方法的定义: 可以看出, Select方法中的参数采用了Func泛型委托,根据泛型委托的定义TSource和TResult分别代表要确定的数据类型以及要返回的数据类型。 <<(2)接下来看看如何在程序中使用Func委托: 首先定义一个与源数据类型不同的新数据类型作为返回值类型: :①标准定义版: 【嘻哈简化版:借助编译器提供的自动识别,简化我们的代码 三绝逼懒人版:借助匿名类和泛型可以大大简化我们的代码 <(3)调试运行可以得到以下结果: <(1)通过Reflector反编译,我们接下来看看编译器帮我们生成的东东: p> 【(2)查看自动生成的委托和方法的定义: 相信经过上节行动的详细分析,这里大家应该也可以举一反三了解编译器我们到底做了什么事情了,这里我就不再赘述了,后面也不会再赘述这方面的东东(为了节省页面大小)。 当然,和Action类似,.NET基类库为我们也提供了多达16输入参数的Func委托,但是,输出参数却只有1个。 /**************************** ************************* 一、委托 1、普通委托不带参数 公共类 TestAction : MonoBehaviour { public delegate void TestDelegate(); void ShowTest() { } public class TestAction : MonoBehaviour { public delegate void TestDelegate(int temp); void ShowTest (int temp) { } public class TestAction : MonoBehaviour { void Start () {< !-- --> void ShowTest(string temp) { } void Start () { String ShowTest(string temp) { } void Start () { String ShowTest() { } public class TestAction : MonoBehaviour { void Start () { int ShowTest(int temp) { } public class TestAction : MonoBehaviour { void Start() { }
Action<参数1, 参数2> 委托名= ((参数1,参数2) => {无返回值的方法体});
1
Func是有返回值的泛型委托
Func<参数1, 参数2, 返回值> 委托名= ((参数1,参数2) => { 带方法体的返回值 });
1
这个委托只能确定一个参数,并且如参参的类型由用户自行提供,返回一个bool类型的值。
Predicate<参数1>委托名= ((参数1) => {带返回值的方法体});
本来呢委托定义(声明)好之后,还得再单独定义委托需要使用的方法。不如说你定义了一个外汇的委托,你还需要重新写计算加减乘除的方法来供外汇委托来使用。这个时候我们就想到了匿名方法。用匿名方法就不需要再单独写加减乘除了这些方法了,只需要在匿名方法的方法里面实现这些逻辑就好了。
delegate int Calculator(int x, int y) ; //委托类型
{
//创建委托对象(确定与哪些方法进行绑定),委托实例名=new委托名(某个)类的方法)本例与加法向绑定
计算器添加 =delegate( int x, int y)
{
return x+y;
};
计算器 移动 = delegate(int x, int y)
{
return x - y;
};
计算器 Multiply = delegate(int x, int y)
{
return x * y;
} ;
计算器 Divide = delegate(int x, int y)
{
return x / y;
};
加(4, 4);//8
移动(4, 4);//0
乘(4, 4);//16
除(4, 4);//1
创建委托实例写法:
委托 委托名=delegate(参数){会调用的方法体};
定义:“Lambda 表达式”是一个匿名函数,是一种高效的类似函数式编程的表达式
委托委托名=((参数1,。。参数n)=>{会调用的方法体});
1.委托实例.Invoke(参数列表)
/********************************* ***使用FuncFunc
不知道童鞋们有没有遇到过这样的问题,在数据读取访问层中数据集合时,发现该方法需要返回的结果中包括对象及其外键对象时也许你会选择用动态对象(动态关键字),不错,这个确实可以解决这个问题,但是有一个弊端,且有动态对象在运行时编译,在编写程序时,它存在一个很不方便的体验,它不能点(.)出它的属性,不免会产生意外的拼写错误或者寻找-复制-粘贴的麻烦。 /p> C# 有返回类型的内置委托—Func
2.1 初识Func
MSDN给出的定义: <
public delegate TResult Func
2.1.1 没有Func时的使用
2.2深入Func
2.2.1最先行:爽一下
// // 摘要: // 将序列中的每个元素投影到新表中。 // // 参数: // source: // 一个值序列,要调用序列转换函数。 // // 选择器: // 评估每个元素的转换函数。 //// 类型参数: // TSource: // source 中元素的类型。 // // TResult: // 选择器返回值的类型。 // // 返回结果: // 一个 System.Collections.Generic.IEnumerable
public class LitePerson { public string Name { get; }放; } }
List
var liteList = personList.Select(delegate(Person p) { return new { Name = p.Name, AddDate = DateTime.现在 }; });
<2.2.2原理为王:探查一次
C#委托、Action、Func、lambda表达式
注意:在使用委托的时候,
1、在A要委托B的时候,在B中声明一个委托,在A中赋值委托B,当B中的条件触发时,回调A中的方法
举例:当在一个界面中,在点击滑动列表中的某个项目是,在界面中显示该项目详细信息时,可以在滑动列表中创建委托,在界面显示中对滑动列表中的委托进行注册,这样当在点击滑动列表项目的时候,调用委托,传送数据,显示界面数据刷新
public TestDelegate test;
void Start () {
test = ShowTest;
test();
}
Debug.Log("普通委托。。。");
}
< br /> 2、普通委托带参数
public TestDelegate test;
void Start () {
test = ShowTest;
test(2);
}
Debug.Log("带参数委托委托。。。" + temp);
}
3、C# Action委托,是不带返回值的委托
Action
testAction("C# Action 测试!");
}
Debug.Log(temp);
}
4、C# Func 委托
//Func
Func
Debug.Log(testFunc("C# Func 测试!"));
}
return temp;
}
公共类 TestAction : MonoBehaviour {
//如果是一个类型Func
Func
Debug.Log(testFunc());
}
return "C# Func 测试,单个参数是返回类型!";
}
5、lambda表达式
注意:主要用于简化函数,代码看起来简洁明了
//第一个参数是确定参数类型,第二个委托返回类型
Func
Debug.Log(testFunc(2));
}
return temp;
}
lambda整体写法
Func
if (p + k == 10)
{
返回 true;
}
else {
返回 false;
}< br /> };
Debug.Log("lambda表达式,返回值测试 " + test(8,8));
}
p>
C泛型概念及方式详解原创由知识百科栏目发布,感谢您对的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“C泛型概念及方式详解原创”