CA1021: Out parametrelerinden kaçının
Özellik | Değer |
---|---|
Kural Kimliği | CA1021 |
Başlık | out parametrelerinden kaçının |
Kategori | Tasarım |
Hataya neden olan veya bozulmayan düzeltme | Yeni |
.NET 8'de varsayılan olarak etkin | Hayır |
Neden
Ortak türdeki genel veya korumalı bir yöntemin parametresi out
vardır.
Varsayılan olarak, bu kural yalnızca dışarıdan görünen türlere bakar, ancak bu yapılandırılabilir.
Kural açıklaması
Türleri başvuruya göre geçirme (veya ref
kullanarakout
) işaretçiler konusunda deneyim, değer türlerinin ve başvuru türlerinin nasıl farklılık gösterdiğini anlama ve birden çok dönüş değeri olan yöntemleri işleme gerekir. Ayrıca ve ref
parametreleri arasındaki out
fark yaygın olarak anlaşılamayabilir.
Bir başvuru türü "başvuruya göre" geçirildiğinde yöntemi, nesnesinin farklı bir örneğini döndürmek için parametresini kullanmayı amaçlıyor. Başvuru türünü başvuruya göre geçirme, çift işaretçi, işaretçi işaretçisi veya çift dolaylı olarak da bilinir. "Değere göre" geçen varsayılan çağırma kuralını kullanarak, başvuru türünü alan bir parametre nesneye zaten bir işaretçi alır. İşaret ettiği nesne değil işaretçi değere göre geçirilir. Değer geçişi, yöntemin işaretçiyi başvuru türünün yeni bir örneğine işaret etmek için değiştiremeyeceği anlamına gelir. Ancak, işaret ettiği nesnenin içeriğini değiştirebilir. Çoğu uygulama için bu yeterlidir ve istenen davranışı verir.
Bir yöntemin farklı bir örnek döndürmesi gerekiyorsa, bunu gerçekleştirmek için yönteminin dönüş değerini kullanın. Dizeler System.String üzerinde çalışan ve bir dizenin yeni bir örneğini döndüren çeşitli yöntemler için sınıfına bakın. Bu model kullanıldığında, çağıranın özgün nesnenin korunup korunmadığına karar vermesi gerekir.
Dönüş değerleri sık kullanılan ve yoğun olarak kullanılan değerler olsa da ve ref
parametrelerinin out
doğru şekilde uygulanması için ara tasarım ve kodlama becerileri gerekir. Genel bir hedef kitle için tasarım yapan kitaplık mimarları, kullanıcıların veya ref
parametreleriyle out
çalışma konusunda yetkin olmasını beklememelidir.
İhlalleri düzeltme
Bir değer türünden kaynaklanan bu kuralın ihlalini düzeltmek için yönteminin nesnesini dönüş değeri olarak döndürmesini sağlayın. Yöntemin birden çok değer döndürmesi gerekiyorsa, değerleri barındıran bir nesnenin tek bir örneğini döndürmek için yeniden tasarlayın.
Başvuru türünden kaynaklanan bu kuralın ihlalini düzeltmek için istenen davranışın başvurunun yeni bir örneğini döndürmek olduğundan emin olun. Bu durumda, yöntemi bunu yapmak için dönüş değerini kullanmalıdır.
Uyarıların ne zaman bastırılması gerekiyor?
Bu kuraldan gelen bir uyarıyı engellemek güvenlidir. Ancak bu tasarım kullanılabilirlik sorunlarına neden olabilir.
Uyarıyı gizleme
Yalnızca tek bir ihlali engellemek istiyorsanız, kuralı devre dışı bırakmak ve sonra yeniden etkinleştirmek için kaynak dosyanıza ön işlemci yönergeleri ekleyin.
#pragma warning disable CA1021
// The code that's violating the rule is on this line.
#pragma warning restore CA1021
Bir dosya, klasör veya projenin kuralını devre dışı bırakmak için, yapılandırma dosyasındaki önem derecesini none
olarak ayarlayın.
[*.{cs,vb}]
dotnet_diagnostic.CA1021.severity = none
Daha fazla bilgi için bkz . Kod analizi uyarılarını gizleme.
Çözümlemek için kod yapılandırma
Bu kuralın kod tabanınızın hangi bölümlerinde çalıştırılacaklarını yapılandırmak için aşağıdaki seçeneği kullanın.
Bu seçeneği yalnızca bu kural, geçerli olduğu tüm kurallar veya bu kategorideki (Tasarım) tüm kurallar için yapılandırabilirsiniz. Daha fazla bilgi için bkz . Kod kalitesi kuralı yapılandırma seçenekleri.
Belirli API yüzeylerini ekleme
Bu kuralın üzerinde çalıştırılacak kod tabanınızın hangi bölümlerini erişilebilirliklerine göre yapılandırabilirsiniz. Örneğin, kuralın yalnızca genel olmayan API yüzeyinde çalıştırılması gerektiğini belirtmek için projenizdeki bir .editorconfig dosyasına aşağıdaki anahtar-değer çiftini ekleyin:
dotnet_code_quality.CAXXXX.api_surface = private, internal
Örnek 1
Aşağıdaki kitaplık, bir sınıfın kullanıcı geri bildirimlerine yanıt oluşturan iki uygulamasını gösterir. İlk uygulama (BadRefAndOut
), kitaplık kullanıcısını üç dönüş değerini yönetmeye zorlar. İkinci uygulama (RedesignedRefAndOut
), verileri tek bir birim olarak yöneten bir kapsayıcı sınıfının (ReplyData
) örneğini döndürerek kullanıcı deneyimini basitleştirir.
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;
}
}
Örnek 2
Aşağıdaki uygulama kullanıcının deneyimini gösterir. Yeniden tasarlanan kitaplığa (UseTheSimplifiedClass
yöntem) yapılan çağrı daha basittir ve yöntemi tarafından döndürülen bilgiler kolayca yönetilir. İki yöntemin çıkışı aynıdır.
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();
}
}
Örnek 3
Aşağıdaki örnek kitaplık, başvuru türleri için parametrelerin nasıl ref
kullanıldığını gösterir ve bu işlevselliği uygulamak için daha iyi bir yol gösterir.
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";
}
}
Örnek 4
Aşağıdaki uygulama, davranışı göstermek için kitaplıktaki her yöntemi çağırır.
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);
}
}
Bu örnek aşağıdaki çıkışı oluşturur:
Changing pointer - passed by value:
12345
12345
Changing pointer - passed by reference:
12345
12345 ABCDE
Passing by return value:
12345 ABCDE
Desen yöntemlerini deneyin
Gibi Bir Şeyi> Dene<desenini System.Int32.TryParseuygulayan yöntemler bu ihlali tetiklemiyor. Aşağıdaki örnekte yöntemini uygulayan bir yapı (değer türü) gösterilmektedir 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;
}
}