前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >CA1045:不要通过引用来传递类型

CA1045:不要通过引用来传递类型

作者头像
呆呆
发布2022-01-10 09:20:53
4500
发布2022-01-10 09:20:53
举报
文章被收录于专栏:centosDai

规则 ID

CA1045

类别

设计

修复是中断修复还是非中断修复

重大

原因

公共类型中的公共或受保护方法有一个 ref 参数,该参数采用基元类型、引用类型或不属于内置类型的值类型。

规则说明

按引用(使用 out 或 ref)传递类型要求具有使用指针的经验,了解值类型和引用类型的不同之处,以及能处理具有多个返回值的方法。 另外,out 和 ref 参数之间的区别并未得到广泛了解。

如果引用类型“按引用”传递,则该方法会使用参数来返回对象的不同实例。 (按引用传递引用类型也称为使用双指针、指向指针的指针或双间接。) 使用“按值”传递这一默认调用约定,采用引用类型的参数已经收到指向对象的指针。 指针(而不是它指向的对象)按值传递。 按值传递表示方法不能更改指针以使其指向引用类型的新实例,但是它可以更改它所指向的对象的内容。 对于大多数应用程序,这就足够了,并生成了所需的行为。

如果方法必须返回不同的实例,请使用该方法的返回值来实现此目的。 有关对字符串执行操作并返回字符串的新实例的各种方法,请参阅 System.String 类。 通过使用此模型,调用方可决定是否保留原始对象。

尽管返回值很常见且被大量使用,但正确应用 out 和 ref 参数需要中间设计和编码技能。 为一般用户进行设计的库架构师不应指望用户能熟练运用 out 或 ref 参数。

备注

如果使用的参数是大型结构,则在按值传递时,复制这些结构所需的其他资源可能会对性能产生影响。 在这些情况下,可考虑使用 ref 或 out 参数。

如何解决冲突

要修复由值类型引起的此规则的冲突,需使方法返回对象作为其返回值。 如果该方法必须返回多个值,请重新设计它以返回保存值的对象的单个实例。

要修复由引用类型引起的此规则的冲突,需确保所需的行为是否为返回引用的新实例。 如果是,则该方法应使用其返回值来执行此操作。

何时禁止显示警告

可禁止显示此规则发出的警告;但这种设计可能会引发可用性问题。

抑制警告

可以通过多种方式来禁止显示代码分析警告,包括禁用项目的规则、使用预处理器指令为特定代码行禁用该规则或应用 SuppressMessageAttribute 特性。 有关详细信息,请参阅如何禁止显示代码分析警告。

配置代码以进行分析

使用下面的选项来配置代码库的哪些部分要运行此规则。

包含特定的 API 图面

你可以仅为此规则、为所有规则或为此类别中的所有规则配置此选项(设计)。 有关详细信息,请参阅代码质量规则配置选项。

包含特定的 API 图面

你可以根据代码库的可访问性,配置要针对其运行此规则的部分。 例如,若要指定规则应仅针对非公共 API 图面运行,请将以下键值对添加到项目中的 .editorconfig 文件:

dotnet_code_quality.CAXXXX.api_surface = private, internal

示例 1

以下库显示生成对用户反馈响应的类的两个实现。 第一个实现 (BadRefAndOut) 强制库用户管理三个返回值。 第二个实现 (RedesignedRefAndOut) 通过返回容器类 (ReplyData) 的实例来简化用户体验,该容器类将数据作为单个单元进行管理。

public enum Actions

{

Unknown,

Discard,

ForwardToManagement,

ForwardToDeveloper

}

public enum TypeOfFeedback

{

Complaint,

Praise,

Suggestion,

Incomprehensible

}

public class BadRefAndOut

{

// Violates rule: DoNotPassTypesByReference.

public static bool ReplyInformation(TypeOfFeedback input,

out string reply, ref Actions action)

{

bool returnReply = false;

string replyText = "Your feedback has been forwarded " +

"to the product manager.";

reply = String.Empty;

switch (input)

{

case TypeOfFeedback.Complaint:

case TypeOfFeedback.Praise:

action = Actions.ForwardToManagement;

reply = "Thank you. " + replyText;

returnReply = true;

break;

case TypeOfFeedback.Suggestion:

action = Actions.ForwardToDeveloper;

reply = replyText;

returnReply = true;

break;

case TypeOfFeedback.Incomprehensible:

default:

action = Actions.Discard;

returnReply = false;

break;

}

return returnReply;

}

}

// Redesigned version does not use out or ref parameters;

// instead, it returns this container type.

public class ReplyData

{

string reply;

Actions action;

bool returnReply;

// Constructors.

public ReplyData()

{

this.reply = String.Empty;

this.action = Actions.Discard;

this.returnReply = false;

}

public ReplyData(Actions action, string reply, bool returnReply)

{

this.reply = reply;

this.action = action;

this.returnReply = returnReply;

}

// Properties.

public string Reply { get { return reply; } }

public Actions Action { get { return action; } }

public override string ToString()

{

return String.Format("Reply: {0} Action: {1} return? {2}",

reply, action.ToString(), returnReply.ToString());

}

}

public class RedesignedRefAndOut

{

public static ReplyData ReplyInformation(TypeOfFeedback input)

{

ReplyData answer;

string replyText = "Your feedback has been forwarded " +

"to the product manager.";

switch (input)

{

case TypeOfFeedback.Complaint:

case TypeOfFeedback.Praise:

answer = new ReplyData(

Actions.ForwardToManagement,

"Thank you. " + replyText,

true);

break;

case TypeOfFeedback.Suggestion:

answer = new ReplyData(

Actions.ForwardToDeveloper,

replyText,

true);

break;

case TypeOfFeedback.Incomprehensible:

default:

answer = new ReplyData();

break;

}

return answer;

}

}

示例 2

以下应用程序说明了用户的体验。 对重新设计的库的调用(UseTheSimplifiedClass 方法)更简单,并且该方法返回的信息非常易于管理。 这两个方法的输出是相同的。

public class UseComplexMethod

{

static void UseTheComplicatedClass()

{

// Using the version with the ref and out parameters.

// You do not have to initialize an out parameter.

string[] reply = new string[5];

// You must initialize a ref parameter.

Actions[] action = {Actions.Unknown,Actions.Unknown,

Actions.Unknown,Actions.Unknown,

Actions.Unknown,Actions.Unknown};

bool[] disposition = new bool[5];

int i = 0;

foreach (TypeOfFeedback t in Enum.GetValues(typeof(TypeOfFeedback)))

{

// The call to the library.

disposition[i] = BadRefAndOut.ReplyInformation(

t, out reply[i], ref action[i]);

Console.WriteLine("Reply: {0} Action: {1} return? {2} ",

reply[i], action[i], disposition[i]);

i++;

}

}

static void UseTheSimplifiedClass()

{

ReplyData[] answer = new ReplyData[5];

int i = 0;

foreach (TypeOfFeedback t in Enum.GetValues(typeof(TypeOfFeedback)))

{

// The call to the library.

answer[i] = RedesignedRefAndOut.ReplyInformation(t);

Console.WriteLine(answer[i++]);

}

}

public static void Main1045()

{

UseTheComplicatedClass();

// Print a blank line in output.

Console.WriteLine("");

UseTheSimplifiedClass();

}

}

示例 3

下面的示例库说明了如何使用引用类型的 ref 参数,并演示了实现此功能的更好方法。

public class ReferenceTypesAndParameters

{

// The following syntax will not work. You cannot make a

// reference type that is passed by value point to a new

// instance. This needs the ref keyword.

public static void BadPassTheObject(string argument)

{

argument = argument + " ABCDE";

}

// The following syntax will work, but is considered bad design.

// It reassigns the argument to point to a new instance of string.

// Violates rule DoNotPassTypesByReference.

public static void PassTheReference(ref string argument)

{

argument = argument + " ABCDE";

}

// The following syntax will work and is a better design.

// It returns the altered argument as a new instance of string.

public static string BetterThanPassTheReference(string argument)

{

return argument + " ABCDE";

}

}

示例 4

下面的应用程序调用库中的每个方法来演示行为。

public class Test

{

public static void Main1045()

{

string s1 = "12345";

string s2 = "12345";

string s3 = "12345";

Console.WriteLine("Changing pointer - passed by value:");

Console.WriteLine(s1);

ReferenceTypesAndParameters.BadPassTheObject(s1);

Console.WriteLine(s1);

Console.WriteLine("Changing pointer - passed by reference:");

Console.WriteLine(s2);

ReferenceTypesAndParameters.PassTheReference(ref s2);

Console.WriteLine(s2);

Console.WriteLine("Passing by return value:");

s3 = ReferenceTypesAndParameters.BetterThanPassTheReference(s3);

Console.WriteLine(s3);

}

}

该示例产生下面的输出:

Changing pointer - passed by value:

12345

12345

Changing pointer - passed by reference:

12345

12345 ABCDE

Passing by return value:

12345 ABCDE

相关规则

CA1021:避免使用 out 参数

本文系外文翻译,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文系外文翻译前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
容器服务
腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档