深入理解C# 3.x的新特性(2):Extension Method[下篇]

四、Extension Method的本质

通过上面一节的介绍,我们知道了在C#中如何去定义一个Extension Method:它是定义在一个Static class中的、第一个Parameter标记为this关键字的Static Method。在这一节中,我们来进一步认识Extension Method。

和C# 3.0的其他新特性相似,Extension Method仅仅是C#这种.NET Programming Language的新特性而已。我们知道,C#是一种典型的编译型的语言,我们编写的Source Code必须先经过和C# Compiler编译成Assembly,才能被CLR加载,被JIT 编译成Machine Instruction并最终被执行。C# 3.0的这些新的特性大都影响Source被C# Compiler编译成Assembly这个阶段,换句话说,这些新特仅仅是Compiler的新特性而已。通过对Compiler进行修正,促使他将C# 3.0引入的新的语法编译成相对应的IL Code,从本质上看,这些IL Code 和原来的IL并没有本质的区别。所有当被编译生成成Assembly被CLR加载、执行的时候,CLR是意识不到这些新的特性的。

从Extension Method的定义我们可看出,Extension Method本质上是一个Static Method。但是我们往往以Instance Method的方式进行调用。C# Compiler的作用很明显:把一个以Instance Method方式调用的Source Code编译成的于对应于传统的Static Method调用的IL Code

虽然Extension Method本质上仅仅是一个Static Class的Static Method成员,但是毕竟和传统的Static Method有所不同:在第一个Parameter前加了一个this关键字。我们现在来看看他们之间的细微的差异。我们先定义一个一般的Static Method:

public static Vector Adds(Vector v, Vector v1)
{
  
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

注:Vector的定义参见《深入理解C# 3.0的新特性(2):Extension Method - Part I》。

我们来看看通过Compiler进行编译生成的IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  
// Code size       50 (0x32)
  .maxstack  2
  .locals init ([
0class Artech.ExtensionMethod.Vector v,
           [
1class Artech.ExtensionMethod.Vector '<>g__initLocal0')
  IL_0000:  nop
  IL_0001:  newobj     instance 
void Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.
1
  IL_0007:  ldloc.
1
  IL_0008:  ldc.r8     
1.
  IL_0011:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_X(float64)
  IL_0016:  nop
  IL_0017:  ldloc.
1
  IL_0018:  ldc.r8     
2.
  IL_0021:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_0026:  nop
  IL_0027:  ldloc.
1
  IL_0028:  stloc.
0
  IL_0029:  ldloc.
0
  IL_002a:  ldloc.
0
  IL_002b:  call       
class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
class Artech.ExtensionMethod.Vector)
  IL_0030:  stloc.
0
  IL_0031:  ret
// end of method Program::Main

对了解IL的人来说,对上面的IL code应该很容易理解。

我们再来看看对于通过下面的方式定义的Extension Method:

public static class Extension
    
{
         
public static Vector Adds(this Vector v, Vector v1)
        
{
            
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
        }

}

对于得IL如下:

.method public hidebysig static class Artech.ExtensionMethod.Vector 
Adds(
class Artech.ExtensionMethod.Vector v,
class Artech.ExtensionMethod.Vector v1) cil managed
{
  .custom instance 
void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 
  
// Code size       53 (0x35)
  .maxstack  3
  .locals init ([
0class Artech.ExtensionMethod.Vector '<>g__initLocal0',
           [
1class Artech.ExtensionMethod.Vector CS$1$0000)
  IL_0000:  nop
  IL_0001:  newobj     instance 
void Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.
0
  IL_0007:  ldloc.
0
  IL_0008:  ldarg.
0
  IL_0009:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_X()
  IL_000e:  ldarg.
1
  IL_000f:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_X()
  IL_0014:  add
  IL_0015:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_X(float64)
  IL_001a:  nop
  IL_001b:  ldloc.
0
  IL_001c:  ldarg.
0
  IL_001d:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_Y()
  IL_0022:  ldarg.
1
  IL_0023:  callvirt   instance float64 Artech.ExtensionMethod.Vector::get_Y()
  IL_0028:  add
  IL_0029:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_002e:  nop
  IL_002f:  ldloc.
0
  IL_0030:  stloc.
1
  IL_0031:  br.s       IL_0033
  IL_0033:  ldloc.
1
  IL_0034:  ret
}
 // end of method Extension::Adds

通过比较,我们发现和上面定义的一般的Static Method生成的IL唯一的区别就是:在Adds方法定义最开始添加了下面一段代码:

.custom instance void [System.Core]System.Runtime.CompilerServices.ExtensionAttribute::.ctor() = ( 01 00 00 00 ) 

这段添加的IL代码很明显,就是在Adds方法上添加一个Customer Attribute:System.Runtime.CompilerServices.ExtensionAttribute。ExtensionAttribute具有如下的定义:

[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class | AttributeTargets.Assembly)]
public sealed class ExtensionAttribute : Attribute
{
}

所以下面Extension Method的定义

public static Vector Adds(this Vector v, Vector v1)
{
            
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

和下面的定义是等效的

[ExtensionAttribute]
public static Vector Adds(Vector v, Vector v1) 
{
            
return new Vector { X = v.X + v1.X, Y = v.Y + v1.Y };
}

但是,System.Runtime.CompilerServices.ExtensionAttribute和其他Custom Attribute不一样,因为它是为了Extension Method的而定义的,我们只能通过添加this Key word的语法来定义Extension Method。所以当我们将System.Runtime.CompilerServices.ExtensionAttribute直接运用到Adds方法会出现下面的Compile Error:

Do not use 'System.Runtime.CompilerServices.ExtensionAttribute'. Use the 'this' keyword instead.

上面我们比较了Extension Method本身IL和一般Static Method IL,现在我们看看当我们以Instance Method方式调用Extension Method的IL。假设我们通过下面的方式调用Adds。 

class Program
    
{
        
static void Main(string[] args)
        
{
 var v 
= new Vector { X = 1, Y = 2 };
           v 
= v.Adds(v);
        }

}

下面是Main Method的IL:

.method private hidebysig static void  Main(string[] args) cil managed
{
  .entrypoint
  
// Code size       50 (0x32)
  .maxstack  2
  .locals init ([
0class Artech.ExtensionMethod.Vector v,
           [
1class Artech.ExtensionMethod.Vector '<>g__initLocal0')
  IL_0000:  nop
  IL_0001:  newobj     instance 
void Artech.ExtensionMethod.Vector::.ctor()
  IL_0006:  stloc.
1
  IL_0007:  ldloc.
1
  IL_0008:  ldc.r8     
1.
  IL_0011:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_X(float64)
  IL_0016:  nop
  IL_0017:  ldloc.
1
  IL_0018:  ldc.r8     
2.
  IL_0021:  callvirt   instance 
void Artech.ExtensionMethod.Vector::set_Y(float64)
  IL_0026:  nop
  IL_0027:  ldloc.
1
  IL_0028:  stloc.
0
  IL_0029:  ldloc.
0
  IL_002a:  ldloc.
0
  IL_002b:  call       
class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
class Artech.ExtensionMethod.Vector)
  IL_0030:  stloc.
0
  IL_0031:  ret
}
 // end of method Program::Main

通过上面的IL,我们看到调用的是Artech.ExtensionMethod.Extension的Adds方法。

IL_002b:  call class Artech.ExtensionMethod.Vector Artech.ExtensionMethod.Extension::Adds(class Artech.ExtensionMethod.Vector,
class Artech.ExtensionMethod.Vector)

通过对IL的分析,我们基本上看出了Extension Method的本质。我们再来简单描述一下对Compiler的编译过程:当Compiler对Adds方法的调用进行编译的过程的时候,它必须判断这个Adds方式是Vector Type的成员还是以Extension Method的方式定义。Extension Method的优先级是最低的,只有确定Vector中没有定义相应的Adds方法的时候,Compiler才会在引用的Namespace中查看这些Namespace中是否定义有对应的Adds Extension Method的Static Class。找到后作进行相应的编译,否则出现编译错误。

五、一个完整的Extension Method的Sample

在介绍了Extension Method的本质之后,我们通过一个相对完整的Sample进一步了解Extension Method的运用,通过这个Sample,我们还可以粗略了解LINQ的原理。

C# 3.0为LINQ定义了一系列的Operator:select, from,where,orderby..., 促使我们按照OO的方式来处理各种各样的数据,比如XML,Relational DB Data,C#中IEnumeratable<T> Object。比如:

var names = new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
var result 
= names.Where(name => name.StartsWith("Tom"));
foreach(var name in result)
{
      Console.WriteLine(name);
}

我们通过上面的Code,从一系列的姓名列表中("Tom Cruise", "Tom Hanks", "Al Pacino", "Harrison Ford")筛选名字(First Name)为Tom的姓名。通过Where Operator,传入一个以Lambda Expression表示的筛选条件(name => name.StartsWith("Tom"))。Where Operator就是通过Extension Method的方式定义的。

在这里提供的Sample就是定义一个完成Where Operator相同功能的Operator,我们把这个Operator起名为When

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;

namespace Artech.ExtensionMethod
{
    
public delegate TResult Function<Tparam, TResult>(Tparam param);

    
public static class Extension
    
{
        
public static IEnumerable<TSource> When<TSource>(this IEnumerable<TSource> source, Function<TSource, bool> predicate)
        
{
            
return new WhenEnumerator<TSource>(source, predicate);
        }
  

    }


    
public class WhenEnumerator<TSource> : IEnumerable<TSource>, IEnumerator<TSource>
    
{
        
private IEnumerable<TSource> _source;
        
private Function<TSource, bool> _predicate;
        
private IEnumerator<TSource> _sourceEnumerator;

        
public WhenEnumerator(IEnumerable<TSource> source, Function<TSource, bool> predicate)
        
{
            
this._source = source;
            
this._predicate = predicate;
            
this._sourceEnumerator = this._source.GetEnumerator();
        }


        
IEnumerable Members#region IEnumerable<TSource> Members

        
public IEnumerator<TSource> GetEnumerator()
        
{
            
return new WhenEnumerator<TSource>(this._source, this._predicate);
        }


        
#endregion


        
IEnumerable Members#region IEnumerable Members

        IEnumerator IEnumerable.GetEnumerator()
        
{
            
throw new Exception("The method or operation is not implemented.");
        }


        
#endregion


        
IEnumerator Members#region IEnumerator<TSource> Members

        
public TSource Current
        
{
            
get return this._sourceEnumerator.Current; }
        }


        
#endregion


        
IDisposable Members#region IDisposable Members

        
public void Dispose()
        
{
            
//throw new Exception("The method or operation is not implemented.");
        }


        
#endregion


        
IEnumerator Members#region IEnumerator Members

        
object IEnumerator.Current
        
{
            
get
            
{
                
return this._sourceEnumerator.Current;
            }

        }


        
public bool MoveNext()
        
{
            
if (!this._sourceEnumerator.MoveNext())
            
{
                
return false;
            }


            
while (!this._predicate(this._sourceEnumerator.Current))
            
{
                
if (!this._sourceEnumerator.MoveNext())
                
{
                    
return false;
                }

            }


            
return true;
        }


        
public void Reset()
        
{
            
this._sourceEnumerator.Reset();
        }


        
#endregion

    }

}

我们来看看我们新的LINQ Operator:When的定义。我首先定义了一个Generic Delegate:Function。实际上他定义了一个一元函数y = f(x),TParam和TResult为参数和返回值得类型。 

public delegate TResult Function<Tparam, TResult>(Tparam param);

接着在Static Class Extesnion中定义了Extension Method:When。该方法包含两个参数,其中一个是执行筛选的数据源,另一个是用于判断数据源每个对象是否满足你所定义的筛选条件的断言。返回一个我们自定义的、实现了IEnumerable的WhenEnumerator对象。

public static class Extension
    
{
        
public static IEnumerable<TSource> When<TSource>(this IEnumerable<TSource> source, Function<TSource, bool> predicate)
        
{
            
return new WhenEnumerator<TSource>(source, predicate);
        }
 
    }

WhenEnumerator的定义是实现When Extension Method的关键,我们现在着重来介绍它的具体实现。WhenEnumerator实现了Interface Enumerable<T>,为了简单,我们也它对应的Enumerator的实现也定义在同一个Class中,所以WhenEnumerator实现了两个Interface:IEnumerable<TSource>, IEnumerator<TSource>。 

以下3个成员分别代表:用于执行筛选的数据源、用于判断是否满足筛选条件的断言以及数据源的Enumerator对象。

private IEnumerable<TSource> _source;
private Function<TSource, bool> _predicate;
private IEnumerator<TSource> _sourceEnumerator;

通过返回一个WhenEnumerator对象,实现了IEnumerable<TSource>的GetEnumerator()方法。 

        public IEnumerator<TSource> GetEnumerator()
        
{
            
return new WhenEnumerator<TSource>(this._source, this._predicate);
        }

对于另一个Interface IEnumerator<TSource>,直接调用数据源的Enumerator的同名方法实现了Current,和Reset()。对于MoveNext()则通过如下的方式实现:把当前的位置设置在下一个满足筛选条件的Element上

public bool MoveNext()
        
{
            
if (!this._sourceEnumerator.MoveNext())
            
{
                
return false;
            }


            
while (!this._predicate(this._sourceEnumerator.Current))
            
{
                
if (!this._sourceEnumerator.MoveNext())
                
{
                    
return false;
                }

            }


            
return true;
        }

到现在为止,这个新的LINQ Operator被创建,现在我们可以按照使用Where operator的方式来调用When。

我们可以通过Delegate的方式来使用When Operator:

class Program
    
{
        
static void Main()
        
{
            var names 
= new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
            var result 
= names.When(delegate(string name) return name.StartsWith("Tom"); });
            
foreach (var name in result)
            
{
                Console.WriteLine(name);
            }

        }

}

输出结果:

Tom Cruise
Tom Hanks

我们也可以通过Lambda Expression的方式来使用When Operator:

static void Main()
        
{
            var names 
= new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
            var result 
= names.When(name=>name.StartsWith("Tom"));
            
foreach (var name in result)
            
{
                Console.WriteLine(name);
            }

        }

显然这种方式更简洁。 

Deferred Evaluation

对于LINQ,有一个非常重要的特征:Deferred Evaluation。在了解这个特征之前,我们来看一个例子:

static void Main()
{
            var names 
= new List<string> "Tom Cruise""Tom Hanks""Al Pacino""Harrison Ford" };
            var result1 
= names.When(name=>name.StartsWith("Tom"));
            names[
0= "Stephen Chou";
            var result2 
= names.When(name => name.StartsWith("Tom"));


            
foreach (var name in result1)
            
{
                Console.WriteLine(name);
            }


            
foreach (var name in result2)
            
{
                Console.WriteLine(name);
            }

}

运行程序,你会发现两个foreach loop显示的结果都是一样的:Tom Hanks。为什么result1实在第一个Element被改动之前返回的,但我们最终输出的结果却反映的是改动之后的数据源。通过我们上面的定义,你很容易得到答案。在这里我要说的是LINQ的一个重要的特性Deferred Evaluation:在调用Operator的时候并不会有任何的任何数据获取的过程,这个阶段的任务是创建一个同于获取数据的表达式。只要你真正所用到这个数据的时候,采用重数据源中通过你构建的表达式通过查询获取数据。

C# 3.x相关内容:
[原创]深入理解C# 3.x的新特性(1):Anonymous Type
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part I
[原创]深入理解C# 3.x的新特性(2):Extension Method - Part II
[原创]深入理解C# 3.x的新特性(3):从Delegate、Anonymous Method到Lambda Expression
[原创]深入理解C# 3.x的新特性(4):Automatically Implemented Property
[原创]深入理解C# 3.x的新特性(5):Object Initializer 和 Collection Initializer

转载于:https://www.cnblogs.com/artech/archive/2007/07/19/823847.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/497071.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

解释器模式详解

Expression.java public interface Expression {// 解析公式和数值&#xff0c;其中var 中的key 值是公式中的参数&#xff0c;value 值是具体的数字.public int interpreter(final Map<String, Integer> var); } VarExpression.java public class VarExpression imp…

二分查找(c++)

二分查找 二分查找也称折半查找&#xff08;Binary Search&#xff09;&#xff0c;它是一种效率较高的查找方法。但是&#xff0c;折半查找要求线性表必须采用顺序存储结构&#xff0c;而且表中元素按关键字有序排列。它充分利用了元素间的次序关系&#xff0c;采用分治策略&a…

谷歌母公司投资成绩:4大机构各有侧重,投资2个马斯克项目

李杉 编译自 TechCrunch量子位 出品 | 公众号 QbitAIAlphabet&#xff0c;谷歌母公司。和谷歌做的大多数事一样&#xff0c;Alphabet也在大规模进行投资。这这篇文章中&#xff0c;外媒TechCrunch盘点了Alphabet的风险投资情况、它的投资组合的表现&#xff0c;以及该公司的投资…

Redis 数据库入门教程

From&#xff1a;http://www.jb51.net/article/56448.htm Redis 菜鸟教程&#xff1a;http://www.runoob.com/redis/redis-tutorial.html Redis 设计与实现&#xff1a;http://redisbook.com/ Redis基础、高级特性与性能调优&#xff1a;https://www.jianshu.com/p/2f14bc57…

大脑的终极秘密——从狮子也有意识谈起

作者&#xff1a;中国科学院大学教授 谢平理性思维是意识的高级成分&#xff0c;但它并非人类的专利。在自然界的生存斗争中&#xff0c;人类不仅仅需要通过感觉器官去获取外部世界的印象&#xff0c;还需要对外部世界的事物进行归类、判断与推理&#xff0c;决定自己对刺激的…

一个线程池中的线程异常了,那么线程池会怎么处理这个线程?

来源&#xff1a;一个线程池中的线程异常了&#xff0c;那么线程池会怎么处理这个线程? - 反光的小鱼儿 - 博客园 一个线程池中的线程异常了&#xff0c;那么线程池会怎么处理这个线程? 目录 线程池常用问题 不允许使用的原因测试流程 测试用例抛出堆栈异常为啥对了一半?怎…

深度学习:技术原理、迭代路径与局限

来源&#xff1a;36氪作者&#xff1a;何沛宽本文尝试复盘梳理深度学习目前的技术要点&#xff0c;深度学习中模型迭代的方向&#xff0c;以及改进后存在的局限。第一部分&#xff1a;深度学习技术基本要素&#xff1a;神经元、神经网络、分类器、可视化框架在深度学习领域&…

简单而直接的Python web 框架:web.py

From&#xff1a;https://www.oschina.net/question/5189_4306 Web.py github 地址&#xff1a;https://github.com/webpy/webpy https://pypi.python.org/pypi/web.py Web.py Cookbook 简体中文版&#xff1a;http://webpy.org/cookbook/index.zh-cn web.py 0.3 新…

从寻找可敬的人类开始,扩展未来人类生存的8个维度

来源&#xff1a;资本实验室作者&#xff1a;李鑫从小村庄到大城市&#xff0c;从国内到国外&#xff0c;从地球到月球&#xff0c;从太阳系到银河系……什么样的距离才是最远的距离&#xff1f;从地球的内部&#xff0c;到每一个原子&#xff0c;再到我们的情绪&#xff0c;哪…

开源 Python网络爬虫框架 Scrapy

开源 Python 网络爬虫框架 Scrapy&#xff1a;http://blog.csdn.net/zbyufei/article/details/7554322 介绍 所谓网络爬虫&#xff0c;就是一个在网上到处或定向抓取数据的程序&#xff0c;当然&#xff0c;这种说法不够专业&#xff0c;更专业的描述就是&#xff0c;抓取特定网…

微服务架构设计模式~为应用程序定义微服务架构

为应用程序定义微服务架构 第一步&#xff1a;定义系统操作 第二步&#xff1a;定义服务 第三步&#xff1a;定义服务API和协作方式 第一步&#xff1a;定义系统操作 第二步&#xff1a;定义服务 第三步&#xff1a;定义服务API和协作方式

用 Python 爬虫框架 Scrapy 爬取心目中的女神

From &#xff1a;http://www.cnblogs.com/wanghzh/p/5824181.html 本博文将带领你从入门到精通爬虫框架 Scrapy&#xff0c;最终具备爬取任何网页的数据的能力。 本文以校花网为例进行爬取&#xff0c;校花网&#xff1a;http://www.xiaohuar.com 让你体验爬取校花的成就感。 …

微服务架构设计模式~识别系统操作

第一步&#xff1a;创建由关键类组成的抽象领域模型&#xff0c;这些关键类提供用于描述系统操作的词汇表&#xff1b; 第二步&#xff1a;确定系统操作&#xff0c;并根据领域模型描述每个系统操作的行为 领域模型主要源自用户故事中提及的名词&#xff0c;系统操作主要来自用…

Facebook、微软、谷歌三大研究巨头齐聚首,共同探讨人工智能发展现状和趋势

作者&#xff1a; 思颖、李诗概要&#xff1a;日前 AAAS 在 reddit 上组织了一场问答&#xff0c;Facebook 人工智能研究院 Yann LeCun&#xff0c;微软研究院院长 Eric Horvitz&#xff0c;谷歌研究总监 Peter Norvig 共同出席此次活动&#xff0c;回答了观众提出的一系列问题…

《大话设计模式》Python 版代码实现

From&#xff1a;http://www.cnblogs.com/wuyuegb2312/archive/2013/04/09/3008320.html 一、简单工厂模式 模式特点&#xff1a;工厂根据条件产生不同功能的类。 程序实例&#xff1a;四则运算计算器&#xff0c;根据用户的输入产生相应的运算类&#xff0c;用这个运算类处理具…

LeCun亲授的深度学习入门课:从飞行器的发明到卷积神经网络

Root 编译整理量子位 出品 | 公众号 QbitAI深度学习和人脑有什么关系&#xff1f;计算机是如何识别各种物体的&#xff1f;我们怎样构建人工大脑&#xff1f;这是深度学习入门者绕不过的几个问题。很幸运&#xff0c;这里有位大牛很乐意为你讲解。2月6日&#xff0c;UCLA&#…

微服务架构设计模式~根据业务能力进行服务拆分

业务能力定义了一个组织的工作 组织的业务能力通常是指这个组织的业务是做什么&#xff0c;它们通常是稳定的。 与之相反&#xff0c;组织采用何种方式来实现它的业务能力&#xff0c;是随着时间不断变化的。 识别业务能力 一个组织有哪些业务能力&#xff0c;是通过对组织的…

微服务架构设计模式~根据子域进行服务拆分

子域 领域驱动为每个子域定义单独的领域模型。子域是领域的一部分&#xff0c;领域是DDD中用来描述应用程序问题域的一个术语。识别子域的方式跟识别业务能力一样&#xff1a;分析业务并识别业务的不同专业领域&#xff0c;分析产出的子域定义结果也会跟业务能力非常接近。 限…

高通:全球NB-IoT/eMTC最新现状

来源&#xff1a;5G概要&#xff1a;全球NB-IoT/eMTC最新现状行业观察未来智能实验室是人工智能学家与科学院相关机构联合成立的人工智能&#xff0c;互联网和脑科学交叉研究机构。由互联网进化论作者&#xff0c;计算机博士刘锋与中国科学院虚拟经济与数据科学研究中心石勇、刘…

2018年看好这些半导体企业

来源&#xff1a;钜亨网对半导体产业来说&#xff0c;去年是一个大年&#xff0c;无论哪个领域&#xff0c;都挣得盘满钵满。进入了2018&#xff0c;半导体产业将会面临哪些新状况&#xff1f;让我们来盘点一下&#xff01;DRAM今年供需稳定记忆体厂商持续获利的好年DRAM价格走…