int转换为cstring_PostgreSQL 隐式类型转换探秘

个人简介

何小栋, 从事产品研发和架构设计工作,对Oracle、PostgreSQL有深入研究,ITPUB数据库版块资深版主。现就职于广州云图数据技术有限公司,系统架构师,博客:http://blog.itpub.net/6906/

摘要

本文通过与Oracle隐式类型转换的比较简要介绍了PostgreSQL中的隐式类型转换,包括Oracle隐式类型转换的机制、PostgreSQL隐式类型转换的机制和原理。

一、缘起

在进入正题前,我们先看下面一个案例,在psql中执行以下SQL:

-- 创建表

testdb=# create table t_cast (id int);

CREATE TABLE

-- 插入数据

testdb=# insert into t_cast values(1),(2),(3);

INSERT 0 3

-- 查询数据

testdb=# select * from t_cast where id = '1';

 id

----

  1

(1 row)

testdb=# select * from t_cast where id = '1'::text;

psql: ERROR:  operator does not exist: integer = text

LINE 1: select * from t_cast where id = '1'::text;

                                      ^

HINT:  No operator matches the given name and argument types. You might need to add explicit type casts.

数据表t_cast中的id列类型为int,执行第一条SQL没有出错,第二条SQL则出错,错误信息为没有相匹配的operator,给出的HINT为添加显式类型转换。对于熟悉Oracle数据库的同学来说,不禁会产生疑问:’1’和’1’::text不都是字符类型吗?两者不是一回事?而同样的SQL,在Oracle中则不会报错,处理机制跟PostgreSQL有什么不同? 

二、Oracle隐式类型转换机制

上面列出的几个问题暂时搁置,我们先来看看同样的SQL在Oracle中的执行结果:

TEST-orcl@DESKTOP-V430TU3>create table t_cast (id int) tablespace users;

Table created.

TEST-orcl@DESKTOP-V430TU3>insert into t_cast values(1);

1 row created.

TEST-orcl@DESKTOP-V430TU3>insert into t_cast values(2);

1 row created.

TEST-orcl@DESKTOP-V430TU3>insert into t_cast values(3);

1 row created.

TEST-orcl@DESKTOP-V430TU3>select * from t_cast where id = '1';

        ID

----------

         1

TEST-orcl@DESKTOP-V430TU3>select * from t_cast where id = cast('1' as varchar2(2));

        ID

----------

         1

在Oracle中不会出错,查阅Oracle文档[1],发现Oracle使用数据类型优先级来判定隐式数据类型转换的先后顺序,优先级由高到低顺序如下:

1. Datetime和interval数据类型

2. BINARY_DOUBLE

3. BINARY_FLOAT

4. NUMBER

5. 字符数据类型

6. 其他内置数据类型

以上例来说,NUMBER与字符数据类型进行等值比较,那么字符类型会自动隐式转换为NUMBER进行比较,而不是把NUMBER转换为字符类型,因为NUMBER优先级高于字符类型。

关于Oracle的隐式转换机制,根据Oracle文档[1],其转换规则如下:

1. 在INSERT/UPDATE操作中,转换数据值为相应的列数据类型

2. 在SELECT FROM操作中,转换列数据类型为目标类型

3. 在操作数值型数据时,调整为最大可用的精度&刻度。在这种情况下,这些操作产生的数据类型可能与基表中的数据类型不同

4. 在比较字符型和数值型数据时,转换字符型为数值型

5. 在字符型/数值型数据与浮点数之间转换时可能不精确,因为字符类型和NUMBER使用十进制精度,而浮点数使用二进制精度

6. 转换CLOB为字符类型(如VARCHAR2),或者转换BLOB转换为RAW时,如果需要转换的数据大小比目标数据类型可表示的要大,则返回错误

7. TIMESTAMP转换为DATE时,时间戳中秒的小数部分将被截断(较老的版本会四舍五入)

8. BINARY_FLOAT转换为BINARY_DOUBLE时不会丢失精度

9. BINARY_DOUBLE转换为BINARY_FLOAT会丢失精度(如使用超过float更大的位数来表示精度)

10. 在比较字符型和日期型数据时,转换字符型为日期型

11. 输入的参数与函数或者操作符参数不匹配时,转换为相应的数据类型

12. 在赋值时,把等号右边的数据转换为左边目标数据类型

13. 在字符串拼接操作时,转换非字符类型为字符类型

14. 在对字符/非字符数据类型进行算术运算/比较时,根据需要会将所有字符类型转换为数值/日期/Rowid

15. 大多数的SQL函数可接受CLOB类型作为参数,这时候会执行CLOB和字符类型之间的转换。如果CLOB大小超过4000Byte,则只获取CLOB中的4000Byte

16. 在RAW/LONG RAW和字符类型之间相互转换时,二进制数据会被表示为十六进制的格式,一个十六进制字符表示RAW数据中的4位

17. 在CHAR和VARCHAR2以及NCHAR和NVARCHAR2之间比较时可能需要不同的字符集。这种情况下默认的转换方向是从数据库字符集转换为国家字符集

更详细的信息请参考Oracle文档。

三、PostgreSQL隐式类型转换机制

这一小节通过回答先前提到几个问题来尝试解析PostgreSQL的隐式类型转换机制。

’1’和’1’::text是一回事吗?

在SQL语句中,’1’和’1’::text是一回事吗?从实际执行结果来看,显然不是一回事,否则使用后者就不会出错了。

一般来说,在PostgreSQL中,SQL语句在执行前需要经过三个步骤:词法分析、语法分析和语义分析。在词法分析阶段,SQL语句中的’1’会被视为string literal(字符串文字),注意是string literal不是string value(字符串值)!string literal意思是在单引号或双引号中的一串字符,在PostgreSQL中如果string literal没有指定类型,那么该string literal的数据类型会被认为是unknown,如指定类型则为指定的数据类型。例如:

select * from t_cast where id = '1';

未指定类型,‘1’类型为unknown

select * from t_cast where id = '1'::text;

指定了数据类型,1’的类型为text

int与text类型比较,为何会报错?

int与text类型比较,为何会报错?实际上,PostgreSQL根据系统目录中的定义来确定类型D1和类型D2能否执行某个操作O。

在示例中,‘=’为二元操作符,左操作数类型为int,右操作数类型为text,在pg_operator系统目录中,不存在该operator和相应操作数类型的定义:

testdb=# select oprname,oprleft::regtype,oprright::regtype,oprcode from pg_operator where oprname='=' and oprleft::regtype='int'::regtype;

 oprname | oprleft | oprright | oprcode

---------+---------+----------+---------

 =       | integer | bigint   | int48eq

 =       | integer | integer  | int4eq

 =       | integer | smallint | int42eq

(3 rows)

testdb=# select oprname,oprleft::regtype,oprright::regtype,oprcode from pg_operator where oprname='=' and oprright::regtype='text'::regtype;

 oprname | oprleft | oprright |  oprcode   

---------+---------+----------+------------

 =       | text    | text     | texteq

 =       | name    | text     | nameeqtext

(2 rows)

而且int和text并没有在pg_cast系统目录中定义为可相互转换:

testdb=# select castsource::regtype,casttarget::regtype,castfunc from pg_cast where castsource::regtype='integer'::regtype;

 castsource |    casttarget    | castfunc

------------+------------------+----------

 integer    | bigint           |      481

 integer    | smallint         |      314

 integer    | real             |      318

 integer    | double precision |      316

 integer    | numeric          |     1740

 integer    | money            |     3811

 integer    | boolean          |     2557

 integer    | oid              |        0

 integer    | regproc          |        0

 integer    | regprocedure     |        0

 integer    | regoper          |        0

 integer    | regoperator      |        0

 integer    | regclass         |        0

 integer    | regtype          |        0

 integer    | regconfig        |        0

 integer    | regdictionary    |        0

 integer    | regrole          |        0

 integer    | regnamespace     |        0

 integer    | "char"           |       78

 integer    | bit              |     1683

(20 rows)

testdb=# select castsource::regtype,casttarget::regtype,castfunc from pg_cast where castsource::regtype='text'::regtype;

 castsource |    casttarget     | castfunc

------------+-------------------+----------

 text       | regclass          |     1079

 text       | character         |        0

 text       | character varying |        0

 text       | "char"            |      944

 text       | name              |      407

 text       | xml               |     2896

(6 rows)

既没有定义operator也没有定义数据类型cast,因此int与text等值比较会报错。

如何确定转换方向?

接下来的一个问题是int类型与unknown类型以及int与text比较,是int转换为text类型还是text类型转换为int类型?是否有优先级之分?

第一个问题,int类型与unknown类型比较的转换方向。在PostgreSQL中,不单是int类型,包括其他确定的数据类型,如存在满足条件的operator,unknown都会转换为确定的数据类型进行比较,转换失败则报错。

例如:

testdb=# select * from t_cast where id = '1.1';

psql: ERROR:  invalid input syntax for type integer: "1.1"

LINE 1: select * from t_cast where id = '1.1';

                                        ^

id为int类型,‘1.1’为unknown类型,PostgreSQL会尝试吧‘1.1’转换为整型与id进行比较,转换不了则失败报错。值得一提的是,报错信息提示语法错误,也就是说PostgreSQL在语法分析阶段已发现错误而不是在执行阶段才发现数据类型转换错误。

第二个问题,int与text比较时的转换方向。刚才已看到,在原生的PostgreSQL中没有定义int与text的转换,但可通过create cast命令自定义类型转换:

testdb=# create cast(integer as text) with inout as implicit;

CREATE CAST

testdb=# create cast(text as integer) with inout as implicit;

CREATE CAST

我们同时定义了int和text的相互转换,那到底使用哪一个CAST呢?

testdb=# explain select * from t_cast where id = '1'::text;

psql: ERROR:  operator is not unique: integer = text

LINE 1: explain select * from t_cast where id = '1'::text;

                                              ^

HINT:  Could not choose a best candidate operator. You might need to add explicit type casts.

PostgreSQL给出的答案是无法处理,无法确定使用哪一个候选而报错。如果我们需要实现Oracle兼容性(转换为NUMBER比较),那么保留text到int的转换即可:

testdb=# drop cast(integer as text);

DROP CAST

testdb=# explain select * from t_cast where id = '1'::text;

                       QUERY PLAN                       

--------------------------------------------------------

 Seq Scan on t_cast  (cost=0.00..41.88 rows=13 width=4)

   Filter: (id = 1)

(2 rows)

除了定义cast来实现int和text的比较外,还可以通过自定义operator来实现,这一部分不在本文的讨论范围,暂且略过。

PostgreSQL的隐式类型转换在官方文档有详细说明[2],这里不再详细展开。

四、PostgreSQL隐式类型转换原理

下面从源码(PostgreSQL 12 beta1)上来对隐式类型转换的原理作一简要解析,包括使用的数据结构FormData_pg_cast以及实现函数make_op等。

数据结构

FormData_pg_cast结构体定义了pg_cast中数据的结构

/* ----------------

 *    pg_cast definition.  cpp turns this into

 *    typedef struct FormData_pg_cast

 * ----------------

 */

CATALOG(pg_cast,2605,CastRelationId)

{

  Oid     oid;      /* oid */

  /* source datatype for cast */

  Oid     castsource BKI_LOOKUP(pg_type);//源类型

  /* destination datatype for cast */

  Oid     casttarget BKI_LOOKUP(pg_type);//目标类型

  /* cast function; 0 = binary coercible */

  Oid     castfunc BKI_LOOKUP(pg_proc);//转换函数

  /* contexts in which cast can be used */

  char    castcontext;//上下文,i-表达式,a-赋值,e-显式类型转换

  /* cast method */

//f-通过castfunc定义的函数转换,b-二元运算符中的转换,i-函数参数转换

  char    castmethod;

} FormData_pg_cast;

/* ----------------

 *    Form_pg_cast corresponds to a pointer to a tuple with

 *    the format of pg_cast relation.

 * ----------------

 */

typedef FormData_pg_cast *Form_pg_cast;

make_op

该函数通过变换操作符表达式以及执行类型转换以确保操作数类型兼容。

相关代码如下:

Expr *

make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,

    Node *last_srf, int location)

{

 /* otherwise, binary operator */

    //二元操作符

    ltypeId = exprType(ltree);

    rtypeId = exprType(rtree);

    tup = oper(pstate, opname, ltypeId, rtypeId, false, location);

    /* otherwise, binary operator */

//二元操作符

    args = list_make2(ltree, rtree);

    actual_arg_types[0] = ltypeId;

    actual_arg_types[1] = rtypeId;

    declared_arg_types[0] = opform->oprleft;//左操作数

    declared_arg_types[1] = opform->oprright;//右操作数

    nargs = 2;

//调用函数make_fn_arguments实现参数类型转换

  make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);

make_fn_arguments

调用coerce_type函数实现转换

void

make_fn_arguments(ParseState *pstate,

          List *fargs,

          Oid *actual_arg_types,

          Oid *declared_arg_types)

{

        …

//执行转换

        node = coerce_type(pstate,// ParseState结构体

                   node,//节点(实际类型为List *)

                   actual_arg_types[i],//实际操作符

                   declared_arg_types[i],//声明操作符 

-1,// targetTypeMod

                   COERCION_IMPLICIT,// CoercionContext

                   COERCE_IMPLICIT_CAST,// CoercionForm

                   -1);// location

coerce_type

该函数判断类型是为unknown,如为unknown则转换为另一确定操作数的数据类型,最终的转换实现在stringTypeDatum中

Node *

coerce_type(ParseState *pstate, Node *node,

      Oid inputTypeId, Oid targetTypeId, int32 targetTypeMod,

      CoercionContext ccontext, CoercionForm cformat, int location)

{

  if (inputTypeId == UNKNOWNOID && IsA(node, Const))

  {

//--------- 输入类型为unknown并且是常量

Const    *con = (Const *) node;//常量

Const    *newcon = makeNode(Const);//转换后的常量

   /*

     * We assume here that UNKNOWN's internal representation is the same

     * as CSTRING.

     * 内部表示跟CSTRING一样

     */

    if (!con->constisnull)

      newcon->constvalue = stringTypeDatum(baseType,

                         DatumGetCString(con->constvalue),

                         inputTypeMod);//调用DatumGetCString函数

    else

      newcon->constvalue = stringTypeDatum(baseType,

                         NULL,

                         inputTypeMod);//NULL值

stringTypeDatum

stringTypeDatum调用了int4in/ pg_strtoint32函数对string literal进行解析

/*

 *    int4in      - converts "num" to int4

 */

Datum

int4in(PG_FUNCTION_ARGS)

{

  char     *num = PG_GETARG_CSTRING(0);

  PG_RETURN_INT32(pg_strtoint32(num));

}

/*

 * Convert input string to a signed 32 bit integer.

 *

 * Allows any number of leading or trailing whitespace characters. Will throw

 * ereport() upon bad input format or overflow.

 *

 * NB: Accumulate input as a negative number, to deal with two's complement

 * representation of the most negative number, which can't be represented as a

 * positive number.

 */

int32

pg_strtoint32(const char *s)

{

  const char *ptr = s;

  int32   tmp = 0;

  bool    neg = false;

  /* skip leading spaces */

  while (likely(*ptr) && isspace((unsigned char) *ptr))

    ptr++;

  /* handle sign */

  if (*ptr == '-')

  {

    ptr++;

    neg = true;

  }

  else if (*ptr == '+')

    ptr++;

  /* require at least one digit */

  if (unlikely(!isdigit((unsigned char) *ptr)))

    goto invalid_syntax;

  /* process digits */

  while (*ptr && isdigit((unsigned char) *ptr))//如'123',-1->-12->-123

  {

//获取数字

    int8    digit = (*ptr++ - '0');//’0’到’9’ASCII值 – ‘0’得到数字

    if (unlikely(pg_mul_s32_overflow(tmp, 10, &tmp)) ||//tmp*10

      unlikely(pg_sub_s32_overflow(tmp, digit, &tmp)))//tmp - digit

      goto out_of_range;

  }

  /* allow trailing whitespace, but not other trailing chars */

  while (*ptr != '\0' && isspace((unsigned char) *ptr))

    ptr++;

  if (unlikely(*ptr != '\0'))

    goto invalid_syntax;

  if (!neg)

  {

    /* could fail if input is most negative number */

    if (unlikely(tmp == PG_INT32_MIN))

      goto out_of_range;

    tmp = -tmp;//取反,-123->123

  }

  return tmp;

out_of_range://越界

  ereport(ERROR,

      (errcode(ERRCODE_NUMERIC_VALUE_OUT_OF_RANGE),

       errmsg("value \"%s\" is out of range for type %s",

          s, "integer")));

invalid_syntax://非法的语法

  ereport(ERROR,

      (errcode(ERRCODE_INVALID_TEXT_REPRESENTATION),

       errmsg("invalid input syntax for type %s: \"%s\"",

          "integer", s)));

  return 0;     /* keep compiler quiet */

}

PostgreSQL相对于Oracle最大的优势在于可以通过阅读源码了解数据库的底层实现原理,真正的做到知其然知其所以然。

五、参考资料

1、Oracle Data Type Comparison Rules

https://docs.oracle.com/database/121/SQLRF/sql_elements002.htm#SQLRF30027

2、PostgreSQL Type Conversion

https://www.postgresql.org/docs/11/typeconv-overview.html

5249d7aee94040e8cfa04b140bbecbd4.png

PostgreSQL中文社区欢迎广大技术人员投稿

投稿邮箱:press@postgres.cn

66c377bfd7e9715d3ead84fe2a1e94c1.png

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

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

相关文章

【转】UML基础: 第 2 部分 - 对象图 (Object Diagram)

对象图是从类图派生的,因此对象图依赖于类图。 对象图表示类图的一个实例。类图和对象图的基本概念是相似的。对象图也表示系统的静态视图,但这个静态视图是系统在特定时刻的快照。 对象图用于呈现一组对象及其关系作为实例。 对象图的目的 图表的目…

外部依赖项很多未定义标识符_从日本编程书籍《我的第一本编程书》中译版看中文例程如何扬长避短——标识符(一)

日本作者平山尚在前言归结了本书的三点独特之处:从始至终只编写一个程序(俄罗斯方块游戏)使用专门的工具绝对面向首次接触程序的人群第一点,优势是一个项目主体贯穿全书,但同时很考验编排顺序,以及技术覆盖…

Qt: QTableView如何获取(行)选中、行切换信息

**情景:**做一个信息表格,需要多个Model切换,必须用QTableView,而不能用QTableWidget,因为后者不可以进行setModel()。 方案: QTableView和选择有关的的信号有: void activated(const QModelI…

动态网站的技术路线_3个好玩实用小网站!闲暇时间不妨打开看看

感谢你关注“最佳应用”每篇文章解决某行业或某人群的一个痛点第八十四期原创文章By:小佳昨天刷抖音听了一首很有魔性的歌曲,结果分享到社交平台,没想到被很多键盘侠喷了,留言全是批判“审美有毒”,这种垃圾歌曲能火就…

【转】1.DThread、ThreadPool、Task、Parallel的基本用法、区别以及弊端

多线程的操作在程序中也是比较常见的,比如开启一个线程执行一些比较耗时的操作(IO操作),而主线程继续执行当前操作,不会造成主线程阻塞。线程又分为前台线程和后台线程,区别是:整个程序必须要运行完前台线程才会退出&a…

vue传值到后端_Vue.js快速入门就从这儿开始特别是后端程序员

自从前后端分离开始变成主流后,曾经的Jsp、FreeMarker、Velocity、Thymeleaf貌似慢慢被遗忘了,取而代之的是兴起的前端主流语言,比如Vue、React和AngularJS介绍VueVue其实是借鉴了 Angular,目前GitHubstar数最多,建议后…

unity3d collider自动调整大小_自动网格组合建模工具Unity游戏素材资源

分享最新的CG教程与素材资讯!人人素材RRCG-专业的CG艺术交流网站点击上方蓝字关注人人素材本游戏资料是自动网格组合建模工具Unity游戏素材资源,大小:735 KB ,格式:unitypackage,使用软件:unity…

【转】2.2[译]async/await中阻塞死锁

这篇博文主要是讲解在async/await中使用阻塞式代码导致死锁的问题,以及如何避免出现这种死锁。内容主要是从作者Stephen Cleary的两篇博文中翻译过来. 原文1:DontBlock on Async Code 原文2:why the AspNetSynchronizationContext was remove…

Java运用自身排序算法将数组或容器进行随机打乱。

基本思路&#xff1a;数组调用Arrays.sort(T[] a,Comparator<? super T> c),对Comparator进行重写。运用Random类 &#xff0c;实现对数字的随机排序。 对数字进行随机排序。代码如下&#xff1a; import java.util.Arrays; import java.util.Comparator; import java…

文本编码解释

一张图解释字符集 举例说明什么是编码&#xff1a; UTF-8编码 等长编码对于英文来说浪费空间&#xff0c;所以出现了变长编码UTF系列&#xff0c;如UTF8&#xff0c;UTF16&#xff0c;UTF32。 UTF8的编码对象是整个Unicode字符集&#xff0c;所以可以表示所有国家的语言而不会…

tkinter label_tkinter做一个简易提词板

我们看综艺的时候&#xff0c;经常能看到现场的提词板。今天我们就用tkinter做一个简单的提词板&#xff0c;用到了tkinter的label来动态显示文字。我们就以最近火爆的《想见你》这首歌为例&#xff0c;按照歌词时间来显示歌词。首先我们要准备好歌词文件&#xff0c;一般是lrc…

web前后端 http转https

1.转换前准备 http转https需要一个证书、本文已ssl证书举例&#xff0c;只有认证的证书才能被认可。阿里云可以申请免费的证书&#xff0c; 但是生成证书需要域名。且域名要绑定ip。故ssl申请前需要域名。可上阿里云购买。 证书申请教程:证书申请 绑定ip:如果没有服务器、也…

Qt开发技巧:编写.pro文件,在构建流程中加入命令行的方法

1、在项目构建前执行命令 在项目构建前执行命令cmd存在问题system(cmd)存在执行多次的问题&#xff0c;可以参考message函数的QMake Manual说明 优化后的表达式如下&#xff0c;这样此命令只会在构建前执行一次&#xff1a; !build_pass:system(cmd) 2、在链接前后执行&#x…

c++清空输入缓冲区_干货 | C++的输入输出方法

C和C并没有将输入与输出实现在语言中&#xff0c;而是在类库中实现。作为C的超集&#xff0c;C继承了C的输入输出方法。同时将输入与输出视为字流。流充当了程序和流源流目标之间的桥梁。本文将介绍C的输入输出方式&#xff0c;并且浅谈C与C均有的输入输出方式对C有一定学习的同…

Qt应用程序发布:Qt应用程序添加版本版权生产商等信息

设置方法 在QMake Manual手册中搜索关于QMAKE_TARGET内容可以看到有如下QMake变量&#xff1a; QMAKE_TARGET_COMPANY&#xff1a;用于指定生产商QMAKE_TARGET_DESCRIPTION&#xff1a;用于描述应用程序QMAKE_TARGET_COPYRIGHT&#xff1a;用于声明版权QMAKE_TARGET_PRODUCT&…

stl源码剖析_STL源码剖析 阅读笔记(二)allocator

一、空间分配器 allocator从使用上看&#xff0c;空间分配在任何语言的任何组件都不需要我们去过多关心&#xff0c;因为语言、组件的底层肯定都比较完整的做了这件事情。从实现上看&#xff0c;学习 allocator 的原理在源码学习中是首当其冲。因为没有空间分配&#xff0c;则无…

easyexcel将对象处理为多列,自增序列

概述 主要记录在开发中遇到的问题&#xff1a;使用easyexcel导出excel&#xff0c;一般数据都是保存在数据库中&#xff0c;如果查询返回的是一个实体类&#xff0c;且里面有嵌套的实体类对象&#xff0c;这时导出的时候要先对查询出的数据进行业务逻辑处理&#xff0c;让它符…

python做excel表格代码_python操作excel表格

我们在写测试用例的时候&#xff0c;是创建一个表格.xlsx&#xff0c;然后把各种条件加到这张表格中去&#xff0c;所以&#xff0c;如何对excel表格操作&#xff0c;是相当重要的一环&#xff0c;那么&#xff0c;接下来&#xff0c;这篇博客就直接教会大家如何通过python去处…

Windows 查看程序ip地址(面对小白)

前言&#xff1a;Windows自带资源管理器可以查看程序的IP地址。下面以微信通话为例&#xff0c;详细步骤如下(面向小白)&#xff1a; 打开任务管理器 打开方法(以下方法都可以)&#xff1a; 键盘按住 【ctrl】 【alt】 【delete】 ,选择【任务管理器】Windows 10 以下系统…

【转】C#中相同不同程序集存在相同的命名空间的时候的冲突解决办法

快速解决办法描述描述: 1.将相同命名空间的不同程序集分别进行取别名&#xff1a;【具体操作:右击相同程序的引用&#xff0c;在别名上修改&#xff0c;默认的为global】。 2.调用&#xff1a;在所在调用文件里面最前面写 extern alias 别名&#xff0c;然后using 别名.Names…