Not
Bu sayfaya erişim yetkilendirme gerektiriyor. Oturum açmayı veya dizinleri değiştirmeyi deneyebilirsiniz.
Bu sayfaya erişim yetkilendirme gerektiriyor. Dizinleri değiştirmeyi deneyebilirsiniz.
Uyarı
Bu makale .NET Framework'e özgüdür. .NET 6 ve sonraki sürümleri de dahil olmak üzere daha yeni .NET uygulamaları için geçerli değildir.
Kod sözleşmeleri .NET Framework kodunda önkoşulları, son önkoşulları ve nesne sabitlerini belirtmek için bir yol sağlar. Önkoşullar, bir yöntem veya özellik girilirken karşılanması gereken gereksinimlerdir. Son koşullar, yöntem veya özellik kodundan çıkış anındaki beklentileri açıklar. Nesne sabitleri, iyi durumda olan bir sınıf için beklenen durumu açıklar.
Uyarı
Kod sözleşmeleri .NET 5+ sürümünde (.NET Core sürümleri dahil) desteklenmez. Bunun yerine Null atanabilir başvuru türlerini kullanmayı göz önünde bulundurun.
Kod anlaşmaları kodunuzu işaretlemeye yönelik sınıfları, derleme zamanı analizi için statik bir çözümleyiciyi ve çalışma zamanı çözümleyicisini içerir. Kod sözleşmelerinin sınıfları ad alanında System.Diagnostics.Contracts bulunabilir.
Kod sözleşmelerinin avantajları şunlardır:
Geliştirilmiş test: Kod sözleşmeleri statik sözleşme doğrulaması, çalışma zamanı denetimi ve belge oluşturma sağlar.
Otomatik test araçları: Önkoşulları karşılamayan anlamsız test bağımsız değişkenlerini filtreleyerek daha anlamlı birim testleri oluşturmak için kod sözleşmelerini kullanabilirsiniz.
Statik doğrulama: Statik denetleyici, programı çalıştırmadan herhangi bir sözleşme ihlali olup olmadığına karar verebilir. Null başvurular, dizi sınırları ve açık sözleşmeler gibi örtük sözleşmeleri denetler.
Başvuru belgeleri: Belge oluşturucu, mevcut XML belge dosyalarını sözleşme bilgileriyle genişletmektedir. Oluşturulan belge sayfalarının sözleşme bölümlerine sahip olması için Sandcastle ile kullanılabilecek stil sayfaları da vardır.
Tüm .NET Framework dilleri sözleşmelerden hemen yararlanabilir; özel bir ayrıştırıcı veya derleyici yazmanız gerekmez. Visual Studio eklentisi, gerçekleştirilecek kod sözleşmesi çözümleme düzeyini belirtmenize olanak tanır. Çözümleyiciler, sözleşmelerin iyi biçimlendirilmiş olduğunu onaylayabilir (tür denetimi ve ad çözümlemesi) ve ortak ara dil (CIL) biçiminde sözleşmelerin derlenmiş bir biçimini üretebilir. Visual Studio'da sözleşme yazma, araç tarafından sağlanan standart IntelliSense'in avantajlarından yararlanmanızı sağlar.
Sözleşme sınıfındaki yöntemlerin çoğu koşullu olarak derlenir; yani, derleyici yalnızca yönergesini kullanarak özel bir simge CONTRACTS_FULL tanımladığınızda bu yöntemlere #define
çağrılar yayar. CONTRACTS_FULL, yönergeleri kullanmadan #ifdef
kodunuzda sözleşmeler yazmanıza olanak tanır; bazıları sözleşmelerle ve bazıları olmadan farklı derlemeler oluşturabilirsiniz.
Kod sözleşmelerini kullanmaya yönelik araçlar ve ayrıntılı yönergeler için bkz. Visual Studio market sitesindeki Kod Sözleşmeleri .
Önkoşulları
yöntemini kullanarak Contract.Requires önkoşulları ifade edebilirsiniz. Önkoşullar, bir yöntem çağrıldığında durumu belirtir. Bunlar genellikle geçerli parametre değerlerini belirtmek için kullanılır. Önkoşullarda bahsedilen tüm üyeler en az yöntemin kendisi kadar erişilebilir olmalıdır; aksi takdirde, ön koşul bir yöntemin tüm çağıranları tarafından anlaşılamayabilir. Koşulun yan etkileri olmamalıdır. Başarısız önkoşulların çalışma zamanı davranışı çalışma zamanı çözümleyicisi tarafından belirlenir.
Örneğin, aşağıdaki önkoşul parametrenin x
null olmayan olması gerektiğini ifade eder.
Contract.Requires(x != null);
Kodunuzun önkoşul hatasında belirli bir özel durum oluşturması gerekiyorsa, Requires'nin genel aşırı yükleme fonksiyonunu aşağıdaki gibi kullanabilirsiniz.
Contract.Requires<ArgumentNullException>(x != null, "x");
Miras Gereklilik Beyanları
Çoğu kod, kod biçiminde bazı parametre doğrulamaları if
-then
-throw
içerir. Sözleşme araçları aşağıdaki durumlarda bu deyimleri önkoşul olarak tanır:
Deyim, bir yöntemdeki diğer deyimlerden önce yer alır.
Bu tür deyimler kümesinin tamamı, Contract, Requires, Ensures veya EnsuresOnThrow metoduna yapılan çağrı gibi açık bir EndContractBlock yöntem çağrısı tarafından takip edilir.
if
-
then
-
throw
Bu formda deyimler göründüğünde, araçlar bunları eski requires
deyimler olarak tanır.
if
-
then
-
throw
dizisini izleyen başka bir sözleşme yoksa, kodu Contract.EndContractBlock yöntemiyle sonlandırın.
if (x == null) throw new ...
Contract.EndContractBlock(); // All previous "if" checks are preconditions
Önceki testteki koşulun olumsuzlanmış bir önkoşul olduğunu unutmayın. (Gerçek önkoşul x != null
olacaktır.) Olumsuz önkoşul oldukça kısıtlıdır: Önceki örnekte gösterildiği gibi yazılmalıdır; yani, else
içermemelidir ve then
yan tümcesinin gövdesi bir throw
ifadesi olmalıdır. Test if
hem saflık hem de görünürlük kurallarına tabidir (bkz . Kullanım Yönergeleri), ancak throw
ifade yalnızca saflık kurallarına tabidir. Ancak, oluşan özel durumun türü, sözleşmenin gerçekleştiği yöntem kadar görünür olmalıdır.
Son Koşullar
Son koşullar, bir yöntemin tamamlandığındaki durum için sözleşmelerdir. Son koşul, bir yöntemden çıkmadan hemen önce denetleniyor. Başarısız son koşullarının çalışma zamanı davranışı çalışma zamanı çözümleyicisi tarafından belirlenir.
Ön koşullardan farklı olarak, son koşullar daha az görünürlüğe sahip üyelere başvurabilir. İstemci, özel durumu kullanarak bir son koşul tarafından ifade edilen bazı bilgileri anlayamaz veya kullanamayabilir, ancak bu, istemcinin yöntemi doğru kullanma becerisini etkilemez.
Standart Son Koşul
yöntemini kullanarak Ensures standart son koşullarını ifade edebilirsiniz. Son koşullar, yöntemin normal şekilde sonlandırılması durumunda olması true
gereken bir koşulu ifade eder.
Contract.Ensures(this.F > 0);
Olağanüstü Son Koşullar
Olağanüstü son koşullar, bir yöntem belirli bir özel durum fırlattığında söz konusu son koşullar true
olmalıdır. Aşağıdaki örnekte gösterildiği gibi yöntemini kullanarak Contract.EnsuresOnThrow bu son koşulları belirtebilirsiniz.
Contract.EnsuresOnThrow<T>(this.F > 0);
Bağımsız değişken, alt türü true
olan bir özel durum fırlatıldığında T
olması gereken koşuldur.
Olağanüstü bir son koşulda kullanılması zor olan bazı özel durum türleri vardır. Örneğin, türü Exception olan T
kullanmak, yığın taşması veya başka bir kontrol edilemeyen özel durum olsa bile, fırlatılan özel durumun türünden bağımsız olarak koşulun garanti edilmesini yöntemin sağlamasını gerektirir. Belirli istisnai durumlar için, örneğin bir InvalidTimeZoneException yöntemi çağrıldığında bir TimeZoneInfo atıldığında, yalnızca istisnai son koşulları kullanmanız gerekir.
Özel Son Koşul
Aşağıdaki yöntemler yalnızca ardıl koşullarda kullanılabilir:
Son koşullarda yöntem dönüş değerlerine, yönteminin dönüş türüyle değiştirilen
Contract.Result<T>()
ifadesiniT
kullanarak başvurabilirsiniz. Derleyici türü çıkaramadığında türü açıkça belirtmeniz gerekir. Örneğin, C# derleyicisi herhangi bir bağımsız değişken almayan yöntemlerin türlerini çıkaramaz, bu nedenle aşağıdaki son koşulu gerektirir:Contract.Ensures(0 <Contract.Result<int>())
Dönüş türünevoid
sahip yöntemler,Contract.Result<T>()
'ye son koşullarında başvuramaz.Bir son durumdaki önceki durum değeri, bir yöntemin veya özelliğin başındaki ifadenin değerini ifade eder.
Contract.OldValue<T>(e)
ifadesini kullanır; buradaT
,e
türündedir. Derleyici türünü çıkarabildiğinde genel tür bağımsız değişkenini kullanmayabilirsiniz. Örneğin, C# derleyicisi bir bağımsız değişken aldığı için türü her zaman belirler.e
içinde neler meydana gelebileceği ve eski bir ifadenin görünebileceği bağlamlar üzerinde çeşitli kısıtlamalar vardır. Eski bir ifade başka bir eski ifade içeremez. En önemlisi, eski bir ifade yöntemin önkoşul durumunda var olan bir değere başvurmalıdır. Başka bir deyişle, yöntemin önkoşulutrue
olduğu sürece değerlendirilebilecek bir ifade olmalıdır. Bu kuralın birkaç örneği aşağıdadır.Değerin yöntemin önkoşul durumunda mevcut olması gerekir. Bir nesnedeki bir alana başvurmak için önkoşullar nesnenin her zaman null olmadığını garanti etmelidir.
Yöntemin eski ifadedeki dönüş değerine başvuramazsınız:
Contract.OldValue(Contract.Result<int>() + x) // ERROR
Eski bir ifadedeki parametrelere
out
başvuramazsınız.Niceleyicinin aralığı yöntemin dönüş değerine bağlıysa, eski bir ifade niceleyicinin bağlı değişkenine bağımlı olamaz:
Contract.ForAll(0, Contract.Result<int>(), i => Contract.OldValue(xs[i]) > 3); // ERROR
Anonim bir temsilcinin parametresine, dizin oluşturucu veya bir yöntem çağrısının bağımsız değişkeni olarak kullanılmadığı sürece eski bir ifade başvuramaz:
Contract.ForAll(0, xs.Length, i => Contract.OldValue(xs[i]) > 3); // OK Contract.ForAll(0, xs.Length, i => Contract.OldValue(i) > 3); // ERROR
Eski ifadenin değeri anonim temsilcinin parametrelerinden herhangi birine bağlıysa ve anonim temsilci ForAll veya Exists yöntemine bir bağımsız değişken olarak sunulmuyorsa, anonim bir temsilcinin gövdesinde eski bir ifade gerçekleşemez.
Method(... (T t) => Contract.OldValue(... t ...) ...); // ERROR
Out
parametreleri bir sorun oluşturur çünkü anlaşmalar yöntemin gövdesinden önce görünür ve çoğu derleyici son koşullarda parametrelere başvurularaout
izin vermez. Bu sorunu çözmek için Contract sınıfı, ValueAtReturn yöntemini sunar, bu yöntemout
parametresine dayalı bir son koşul oluşturulmasına olanak tanır.public void OutParam(out int x) { Contract.Ensures(Contract.ValueAtReturn(out x) == 3); x = 3; }
yönteminde OldValue olduğu gibi, derleyici türünü çıkarabildiğinde genel tür parametresini atlayabilirsiniz. Sözleşme yeniden yazıcı, yöntem çağrısını
out
parametresinin değeriyle değiştirir. ValueAtReturn yöntemi yalnızca son koşullarda görünebilir. Yöntemin bağımsız değişkeni birout
parametre veya bir yapıout
parametresinin alanı olmalıdır. İkincisi, bir yapı oluşturucusunun son durumu içindeki alanlara atıfta bulunurken de yararlıdır.Uyarı
Şu anda kod sözleşmesi çözümleme araçları
out
parametrelerinin düzgün şekilde başlatılıp başlatılmadığını denetlemez ve bunların son koşulda belirtilmelerini göz ardı eder. Bu nedenle, önceki örnekte, sözleşmeden sonraki satır ona bir tamsayı atamak yerine değerinix
'dan alsaydı, derleyici gerekli hatayı vermezdi. Ancak, CONTRACTS_FULL ön işlemci simgesinin tanımlanmadığı bir derlemede (örneğin, yayın derlemesi), derleyici bir hata döndürür.
Değişmezler
Nesne sabitleri, bir sınıfın her örneği için nesne bir istemciye her görünür olduğunda doğru olması gereken koşullardır. Nesnenin doğru olarak kabul edildiği koşulları ifade eder.
Sabit yöntemler özniteliğiyle ContractInvariantMethodAttribute işaretlenerek tanımlanır. Sabit yöntemler, aşağıdaki örnekte gösterildiği gibi yöntemine Invariant yapılan çağrı dizisi dışında kod içermemelidir. Bunların her biri tek bir sabit değer belirtir.
[ContractInvariantMethod]
protected void ObjectInvariant ()
{
Contract.Invariant(this.y >= 0);
Contract.Invariant(this.x > this.y);
...
}
Sabit değerler, CONTRACTS_FULL ön işlemci simgesi tarafından koşullu olarak tanımlanır. Çalışma zamanı denetimi sırasında, her genel yöntemin sonunda değişmezler denetleniyor. Bir sabit, aynı sınıftaki bir public yöntemden bahsederse, normalde bu public yöntemin sonunda gerçekleşmesi gereken sabit kontrolü devre dışı bırakılır. Bunun yerine, denetim yalnızca bu sınıfa en dıştaki yöntem çağrısının sonunda gerçekleşir. Başka bir sınıftaki bir yöntemin çağrılması nedeniyle sınıfa yeniden girilirse bu durum da ortaya çıkar. Değişmezler, nesne sonlandırıcısı ve IDisposable.Dispose uygulaması için denetlenmiyor.
Kullanım Yönergeleri
Sözleşme Siparişi
Aşağıdaki tabloda, yöntem sözleşmeleri yazarken kullanmanız gereken öğelerin sırası gösterilmektedir.
If-then-throw statements |
Geriye dönük uyumlu genel önkoşullar |
---|---|
Requires | Tüm genel önkoşullar. |
Ensures | Tüm genel (normal) son koşullar. |
EnsuresOnThrow | Tüm genel istisnai sonrası koşullar. |
Ensures | Tüm özel/iç (normal) son koşullar. |
EnsuresOnThrow | Tüm özel/dahili istisnai son durumlar. |
EndContractBlock | Stil önkoşullarını başka sözleşmeler olmadan kullanıyorsanız if -then -throw tarzı 'if' kontrolleri yaparken, tüm önceki 'if' denetimlerinin önkoşul olduğunu belirtmek için EndContractBlock çağrısı yapın. |
Saflık
Bir sözleşme içinde çağrılan tüm yöntemler saf olmalıdır; başka bir ifadeyle, önceden var olan herhangi bir durumu güncelleştirmemeleri gerekir. Saf yöntemin, saf yönteme girdikten sonra oluşturulan nesneleri değiştirmesine izin verilir.
Kod sözleşmesi araçları şu anda aşağıdaki kod öğelerinin saf olduğunu varsayar:
ile PureAttributeişaretlenmiş yöntemler.
ile PureAttribute işaretlenmiş türler (öznitelik, türün tüm yöntemleri için geçerlidir).
Özellik alma erişimcileri.
İşleçler (adları "op" ile başlayan ve bir veya iki parametresi ve geçersiz olmayan dönüş türü olan statik yöntemler).
Tam adı "System.Diagnostics.Contracts.Contract", "System.String", "System.IO.Path" veya "System.Type" ile başlayan herhangi bir yöntem.
PureAttribute ile nitelendirilmiş temsilci türü kendisi olduğu sürece, çağrılan herhangi bir temsilci. Temsilci türleri System.Predicate<T> ve System.Comparison<T> saf kabul edilir.
Görünürlük
Bir sözleşmede bahsedilen tüm üyeler en azından göründükleri yöntem kadar görünür olmalıdır. Örneğin, özel bir alandan genel bir yöntemin önkoşulunda bahsedilemez; istemcileri yöntemi çağırmadan önce böyle bir sözleşmeyi doğrulayamaz. Ancak, alan ile ContractPublicPropertyNameAttributeişaretlenmişse, bu kurallardan muaftır.
Örnek
Aşağıdaki örnekte kod sözleşmelerinin kullanımı gösterilmektedir.
#define CONTRACTS_FULL
using System;
using System.Diagnostics.Contracts;
// An IArray is an ordered collection of objects.
[ContractClass(typeof(IArrayContract))]
public interface IArray
{
// The Item property provides methods to read and edit entries in the array.
Object this[int index]
{
get;
set;
}
int Count
{
get;
}
// Adds an item to the list.
// The return value is the position the new element was inserted in.
int Add(Object value);
// Removes all items from the list.
void Clear();
// Inserts value into the array at position index.
// index must be non-negative and less than or equal to the
// number of elements in the array. If index equals the number
// of items in the array, then value is appended to the end.
void Insert(int index, Object value);
// Removes the item at position index.
void RemoveAt(int index);
}
[ContractClassFor(typeof(IArray))]
internal abstract class IArrayContract : IArray
{
int IArray.Add(Object value)
{
// Returns the index in which an item was inserted.
Contract.Ensures(Contract.Result<int>() >= -1);
Contract.Ensures(Contract.Result<int>() < ((IArray)this).Count);
return default(int);
}
Object IArray.this[int index]
{
get
{
Contract.Requires(index >= 0);
Contract.Requires(index < ((IArray)this).Count);
return default(int);
}
set
{
Contract.Requires(index >= 0);
Contract.Requires(index < ((IArray)this).Count);
}
}
public int Count
{
get
{
Contract.Requires(Count >= 0);
Contract.Requires(Count <= ((IArray)this).Count);
return default(int);
}
}
void IArray.Clear()
{
Contract.Ensures(((IArray)this).Count == 0);
}
void IArray.Insert(int index, Object value)
{
Contract.Requires(index >= 0);
Contract.Requires(index <= ((IArray)this).Count); // For inserting immediately after the end.
Contract.Ensures(((IArray)this).Count == Contract.OldValue(((IArray)this).Count) + 1);
}
void IArray.RemoveAt(int index)
{
Contract.Requires(index >= 0);
Contract.Requires(index < ((IArray)this).Count);
Contract.Ensures(((IArray)this).Count == Contract.OldValue(((IArray)this).Count) - 1);
}
}
#Const CONTRACTS_FULL = True
Imports System.Diagnostics.Contracts
' An IArray is an ordered collection of objects.
<ContractClass(GetType(IArrayContract))> _
Public Interface IArray
' The Item property provides methods to read and edit entries in the array.
Default Property Item(ByVal index As Integer) As [Object]
ReadOnly Property Count() As Integer
' Adds an item to the list.
' The return value is the position the new element was inserted in.
Function Add(ByVal value As Object) As Integer
' Removes all items from the list.
Sub Clear()
' Inserts value into the array at position index.
' index must be non-negative and less than or equal to the
' number of elements in the array. If index equals the number
' of items in the array, then value is appended to the end.
Sub Insert(ByVal index As Integer, ByVal value As [Object])
' Removes the item at position index.
Sub RemoveAt(ByVal index As Integer)
End Interface 'IArray
<ContractClassFor(GetType(IArray))> _
Friend MustInherit Class IArrayContract
Implements IArray
Function Add(ByVal value As Object) As Integer Implements IArray.Add
' Returns the index in which an item was inserted.
Contract.Ensures(Contract.Result(Of Integer)() >= -1) '
Contract.Ensures(Contract.Result(Of Integer)() < CType(Me, IArray).Count) '
Return 0
End Function 'IArray.Add
Default Property Item(ByVal index As Integer) As Object Implements IArray.Item
Get
Contract.Requires(index >= 0)
Contract.Requires(index < CType(Me, IArray).Count)
Return 0 '
End Get
Set(ByVal value As [Object])
Contract.Requires(index >= 0)
Contract.Requires(index < CType(Me, IArray).Count)
End Set
End Property
Public ReadOnly Property Count() As Integer Implements IArray.Count
Get
Contract.Requires(Count >= 0)
Contract.Requires(Count <= CType(Me, IArray).Count)
Return 0 '
End Get
End Property
Sub Clear() Implements IArray.Clear
Contract.Ensures(CType(Me, IArray).Count = 0)
End Sub
Sub Insert(ByVal index As Integer, ByVal value As [Object]) Implements IArray.Insert
Contract.Requires(index >= 0)
Contract.Requires(index <= CType(Me, IArray).Count) ' For inserting immediately after the end.
Contract.Ensures(CType(Me, IArray).Count = Contract.OldValue(CType(Me, IArray).Count) + 1)
End Sub
Sub RemoveAt(ByVal index As Integer) Implements IArray.RemoveAt
Contract.Requires(index >= 0)
Contract.Requires(index < CType(Me, IArray).Count)
Contract.Ensures(CType(Me, IArray).Count = Contract.OldValue(CType(Me, IArray).Count) - 1)
End Sub
End Class