Delen via


CA1045: Geef geen typen door op verwijzing

Eigenschappen Weergegeven als
Regel-id CA1045
Titel Typen niet doorgeven per verwijzing
Categorie Ontwerpen
Oplossing is brekend of niet-brekend Breken
Standaard ingeschakeld in .NET 8 Nee

Oorzaak

Een openbare of beveiligde methode in een openbaar type heeft een parameter die een ref primitief type, een verwijzingstype of een waardetype gebruikt dat geen van de ingebouwde typen is.

Beschrijving van regel

Het doorgeven van typen per verwijzing (met of outref) vereist ervaring met aanwijzers, het begrijpen hoe waardetypen en verwijzingstypen verschillen en methoden verwerken die meerdere retourwaarden hebben. Ook wordt het verschil tussen out en ref parameters niet algemeen begrepen.

Wanneer een verwijzingstype wordt doorgegeven doorverwijzing, is de methode van plan de parameter te gebruiken om een ander exemplaar van het object te retourneren. (Het doorgeven van een verwijzingstype per verwijzing wordt ook wel aangeduid als het gebruik van een dubbele aanwijzer, aanwijzer naar een aanwijzer of dubbele indirectie.) Met behulp van de standaardconventie voor aanroepen, die 'op waarde' wordt doorgegeven, ontvangt een parameter die een verwijzingstype gebruikt al een aanwijzer naar het object. De aanwijzer, niet het object waarnaar het verwijst, wordt doorgegeven door een waarde. Doorgeven door waarde betekent dat de methode de aanwijzer niet kan wijzigen zodat deze verwijst naar een nieuw exemplaar van het verwijzingstype, maar de inhoud van het object waarnaar deze verwijst, kan wijzigen. Voor de meeste toepassingen is dit voldoende en levert dit het gewenste gedrag op.

Als een methode een ander exemplaar moet retourneren, gebruikt u de retourwaarde van de methode om dit te bereiken. Zie de System.String klasse voor verschillende methoden die op tekenreeksen werken en een nieuw exemplaar van een tekenreeks retourneren. Door dit model te gebruiken, wordt het aan de aanroeper overgelaten om te bepalen of het oorspronkelijke object behouden blijft.

Hoewel retourwaarden gebruikelijk en intensief worden gebruikt, is voor de juiste toepassing van out en ref parameters tussenliggende ontwerp- en coderingsvaardigheden vereist. Bibliotheekarchitecten die voor een algemeen publiek ontwerpen, mogen niet verwachten dat gebruikers bekwaam worden in het werken met out of ref parameters.

Notitie

Wanneer u werkt met parameters die grote structuren zijn, kunnen de extra resources die nodig zijn om deze structuren te kopiëren, een prestatie-effect veroorzaken wanneer u de waarde doorgeeft. In dergelijke gevallen kunt u overwegen om parameters te gebruiken ref of out te gebruiken.

Schendingen oplossen

Als u een schending van deze regel wilt oplossen die wordt veroorzaakt door een waardetype, moet u ervoor zorgen dat de methode het object retourneert als retourwaarde. Als de methode meerdere waarden moet retourneren, ontwerpt u deze opnieuw om één exemplaar te retourneren van een object dat de waarden bevat.

Als u een schending van deze regel wilt oplossen die wordt veroorzaakt door een verwijzingstype, moet u ervoor zorgen dat het gewenste gedrag een nieuw exemplaar van de verwijzing retourneert. Als dit het geval is, moet de methode de retourwaarde gebruiken om dit te doen.

Wanneer waarschuwingen onderdrukken

Het is veilig om een waarschuwing van deze regel te onderdrukken; Dit ontwerp kan echter bruikbaarheidsproblemen veroorzaken.

Een waarschuwing onderdrukken

Als u slechts één schending wilt onderdrukken, voegt u preprocessorrichtlijnen toe aan uw bronbestand om de regel uit te schakelen en vervolgens opnieuw in te schakelen.

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

Als u de regel voor een bestand, map of project wilt uitschakelen, stelt u de ernst none ervan in op het configuratiebestand.

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

Zie Codeanalysewaarschuwingen onderdrukken voor meer informatie.

Code configureren om te analyseren

Gebruik de volgende optie om te configureren op welke onderdelen van uw codebase deze regel moet worden uitgevoerd.

U kunt deze optie configureren voor alleen deze regel, voor alle regels waarop deze van toepassing is, of voor alle regels in deze categorie (ontwerp) waarop deze van toepassing is. Zie de configuratieopties voor de codekwaliteitsregel voor meer informatie.

Specifieke API-oppervlakken opnemen

U kunt instellen op welke onderdelen van uw codebase deze regel moet worden uitgevoerd, op basis van hun toegankelijkheid. Als u bijvoorbeeld wilt opgeven dat de regel alleen moet worden uitgevoerd op het niet-openbare API-oppervlak, voegt u het volgende sleutel-waardepaar toe aan een .editorconfig-bestand in uw project:

dotnet_code_quality.CAXXXX.api_surface = private, internal

Voorbeeld 1

In de volgende bibliotheek ziet u twee implementaties van een klasse die reacties genereert op de feedback van de gebruiker. De eerste implementatie (BadRefAndOut) dwingt de bibliotheekgebruiker drie retourwaarden te beheren. De tweede implementatie (RedesignedRefAndOut) vereenvoudigt de gebruikerservaring door een exemplaar van een containerklasse (ReplyData) te retourneren dat de gegevens als één eenheid beheert.

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;
    }
}

Voorbeeld 2

De volgende toepassing illustreert de ervaring van de gebruiker. De aanroep van de opnieuw ontworpen bibliotheek (UseTheSimplifiedClass methode) is eenvoudiger en de informatie die door de methode wordt geretourneerd, wordt eenvoudig beheerd. De uitvoer van de twee methoden is identiek.

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();
    }
}

Voorbeeld 3

In de volgende voorbeeldbibliotheek ziet u hoe ref parameters voor referentietypen worden gebruikt en ziet u een betere manier om deze functionaliteit te implementeren.

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";
    }
}

Voorbeeld 4

Met de volgende toepassing wordt elke methode in de bibliotheek aangeroepen om het gedrag te demonstreren.

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);
    }
}

In dit voorbeeld wordt de volgende uitvoer gegenereerd:

Changing pointer - passed by value:
12345
12345
Changing pointer - passed by reference:
12345
12345 ABCDE
Passing by return value:
12345 ABCDE

CA1021: Parameters vermijden