次の方法で共有


CA1021: out パラメーターを使用しません

プロパティ
ルール ID CA1021
Title out パラメーターを使用しません
[カテゴリ] デザイン
修正が中断ありか中断なしか あり
.NET 9 では既定で有効 いいえ

原因

public 型の public または protected メソッドには、out パラメーターがあります。

既定では、この規則の対象は外部から参照できる型のみですが、これは構成可能です。

規則の説明

参照 (out または ref を使用) で型を渡すには、ポインターの使用経験、値の型と参照型の違いの理解、および複数の戻り値を持つメソッドの処理が必要です。 また、out パラメーターと ref パラメーターの違いはあまり理解されていません。

参照型が "参照渡し" によって渡されると、メソッドはパラメーターを使用してオブジェクトの別のインスタンスを返そうとします。 参照型を参照渡しで渡すことは、二重ポインター、ポインターへのポインター、または二重間接参照とも呼ばれます。 既定の呼び出し規則 ("値渡し") を使用すると、参照型を受け取るパラメーターは既にオブジェクトへのポインターを受け取っています。 ポインターが指すオブジェクトではなく、ポインターが値によって渡されます。 値渡しとは、メソッドで参照型の新しいインスタンスを指すようにポインターを変更できないことを意味します。 ただし、それが指しているオブジェクトの内容は変更できます。 ほとんどのアプリケーションでは、これで十分であり、必要な動作が得られます。

メソッドで異なるインスタンスを返す必要がある場合は、メソッドの戻り値を使用してこれを実現します。 文字列を操作し、文字列の新しいインスタンスを返すさまざまなメソッドについては、System.String クラスを参照してください。 このモデルを使用するときは、元のオブジェクトを保持するかどうかを、呼び出し元が決定する必要があります。

戻り値は一般的であり、よく使用されますが、out および ref パラメーターを正しく適用するには、中間の設計とコーディングのスキルが必要です。 開発者全般に向けてライブラリをデザインする場合、ユーザーが out パラメーターまたは ref パラメーターの扱い方を習得することは期待しないでください。

違反の修正方法

値の型に起因するこの規則違反を修正するには、メソッドからの戻り値としてオブジェクトを返すようにします。 メソッドで複数の値を返す必要がある場合は、値を保持するオブジェクトの 1 つのインスタンスを返すように再設計します。

参照型に起因するこの規則違反を修正するには、必要な動作が参照の新しいインスタンスを返すことであることを確認します。 そうである場合は、メソッドで戻り値を使用してこれを行う必要があります。

どのようなときに警告を抑制するか

この規則からの警告は抑制しても安全です。 ただし、この設計により使いやすさの問題が発生する可能性があります。

警告を抑制する

単一の違反を抑制するだけの場合は、ソース ファイルにプリプロセッサ ディレクティブを追加して無効にしてから、規則をもう一度有効にします。

#pragma warning disable CA1021
// The code that's violating the rule is on this line.
#pragma warning restore CA1021

ファイル、フォルダー、またはプロジェクトの規則を無効にするには、構成ファイルでその重要度を none に設定します。

[*.{cs,vb}]
dotnet_diagnostic.CA1021.severity = none

詳細については、「コード分析の警告を抑制する方法」を参照してください。

分析するコードを構成する

次のオプションを使用して、コードベースのどの部分に対してこの規則を実行するか構成します。

このオプションを構成できる対象は、この規則だけ、それを適用するすべての規則、それを適用するこのカテゴリ (デザイン) のすべての規則のいずれかです。 詳細については、「コード品質規則の構成オプション」を参照してください。

特定の API サーフェイスを含める

ユーザー補助に基づいて、この規則を実行するコードベースの部分を構成できます。 たとえば、非パブリック API サーフェイスでのみ規則を実行するように指定するには、プロジェクトの .editorconfig ファイルに次のキーと値のペアを追加します。

dotnet_code_quality.CAXXXX.api_surface = private, internal

例 1

次のライブラリでは、ユーザーのフィードバックへの応答を生成するクラスの 2 つの実装が示されています。 1 つ目の実装 (BadRefAndOut) を使用すると、ライブラリのユーザーが 3 つの戻り値を管理する必要があります。 2 つ目の実装 (RedesignedRefAndOut) を使用すると、データを 1 つの単位として管理するコンテナー クラス (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
{
    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; }
    public Actions Action { get; }

    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 メソッド) の呼び出しはより簡単で、メソッドによって返される情報を簡単に管理できます。 2 つのメソッドからの出力は同じです。

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 UseClasses()
    {
        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 += " ABCDE";
    }

    // The following syntax works, 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 += " ABCDE";
    }

    // The following syntax works 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 MainTest()
    {
        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

Try パターン メソッド

Try<Something> パターンが実装されているメソッド (System.Int32.TryParse など) の場合、この違反は発生しません。 次の例では、System.Int32.TryParse メソッドを実装する構造体 (値の型) を示します。

public struct Point
{
    public Point(int axisX, int axisY)
    {
        X = axisX;
        Y = axisY;
    }

    public int X { get; }

    public int Y { get; }

    public override int GetHashCode()
    {
        return X ^ Y;
    }

    public override bool Equals(object? obj)
    {
        if (!(obj is Point))
            return false;

        return Equals((Point)obj);
    }

    public bool Equals(Point other)
    {
        if (X != other.X)
            return false;

        return Y == other.Y;
    }

    public static bool operator ==(Point point1, Point point2)
    {
        return point1.Equals(point2);
    }

    public static bool operator !=(Point point1, Point point2)
    {
        return !point1.Equals(point2);
    }

    // Does not violate this rule
    public static bool TryParse(string value, out Point result)
    {
        // TryParse Implementation
        result = new Point(0, 0);
        return false;
    }
}

CA1045: 型を参照によって渡しません