Güvenli Olmayan Evrim

Şampiyonluk sorunu: https://github.com/dotnet/csharplang/issues/9704

Summary

C# dilinde tanımını unsafe , işaretçi türlerinin kullanıldığı konumlara başvurarak çalışma zamanı tarafından yönetilmeyen belleğin başvurulmadığı konumlar olacak şekilde güncelleştiriyoruz. Bu konumlar, bellek güvenliğinin olmadığı yerlerdir ve bellek güvenliği sorunları olarak sınıflandırılan CVE'lerin (Ortak Güvenlik Açıkları ve Etkilenmeler) büyük bir kısmından sorumludur.

// Under the proposed rules:
void M()
{
    int i = 1;
    int* ptr = &i; // Allowed: creating a pointer is not itself unsafe
    unsafe
    {
        Console.WriteLine(*ptr); // Dereference of memory not managed by the runtime. This is unsafe.
        ref int intRef = Unsafe.AsRef(ptr); // Conversion of memory not managed by the runtime to a `ref`. This is unsafe.
    }
}

namespace System.Runtime.CompilerServices
{
    public static class Unsafe
    {
        unsafe public static ref T AsRef<T>(void* source) { /* ... */ } // `unsafe` marks the member as *requires-unsafe*.
    }
}

Motivasyon

Bu özelliğin arka planı, https://github.com/dotnet/designs/blob/main/accepted/2025/memory-safety/caller-unsafe.mdbu teklifin bir parçası olarak gerekli olacak daha geniş ekosistem değişikliklerini izleyen içinde de bulunabilir. Bunlara, yöntemlerin güvenli olmadığı gibi düzgün bir şekilde açıklama eklemeye yönelik BCL güncelleştirmelerinin yanı sıra bellek güvensizliğini nerede oluştuğunun daha iyi anlaşılması için araç güncelleştirmeleri dahildir. Özellikle C# için, bellek güvenli olmamasının dil tarafından düzgün bir şekilde izlendiğinden emin olmak istiyoruz; bugün, bir programa bütünsel olarak bakmak ve bellek güvensizliğini ortaya çıkan tüm konumları anlamak zor olabilir. Bunun nedeni, , System.Runtime.CompilerServices.Unsafeve gibi System.Runtime.InteropServices.Marshalçeşitli yardımcıların bellek güvenliğini ihlal ettiklerini ve özel dikkate ihtiyaçları olduğunu ifade etmemeleridir. Daha sonra bu yardımcıları kullanan yöntemler hemen belli değildir ve bellek güvenliği sorunları için kod denetimi yaparken (gözden geçirme yapılırken veya bildirilen bir güvenlik açığının nedenini belirlemeye çalışırken) sorunlara katkıda bulunabilecek konumları saptamak zor olabilir.

unsafe Geçmişte C# dilinde belirli bir bellek güvenliği deliğine değinilmiştir: işaretçi türlerinin varlığı. İşaretçi türünün artık söz konusu olmadığı anda, C# belleğin güvenli olmamasının kodda gizli kalması için son derece mutlu olur. C# ve .NET ekosistemindeki bu evrimi unsafe ele almak istediğimiz bu sorun, bellek güvensizliği olabilecek alanları etiketlemek, gözden geçirenlerin ve denetçilerin programdaki olası bellek güvensizliği sınırlarını anlamasını kolaylaştırmaktadır. Önemli olan, bunun anlamı unsafe anlamına gelir, sadece onu büyütmek değil. bir işaretçinin varlığı güvenli değildir; güvenli olmayan eylem işaretçinin başvurularını kaldırıyor. Bu, türlerin kendilerini daha da genişletir; türleri doğası gereği güvenli olamaz. Yalnızca güvenli olmayan bir tür kullanma eylemidir, bu türün varlığı değildir.

Bu bilgilerin sistemde akması için yöntemleri olarak unsafeişaretlemenin bir yolunu bulmalıyız. Bugün, unsafe bir yöntem değiştiricinin dış etkisi olmadığından, yalnızca üyenin imzasında ve gövdesinde işaretçilerin kullanılmasına izin verir. Bundan sonra, unsafe değiştirici üyenin anlamını genel olarak değiştireceği için, üyenin bellek güvenliğiyle ilgili endişeleri olduğunu ve tüm kullanımların üyeyi kullanan programcı tarafından el ile doğrulanması gerektiğini gösterir. Bu, mevcut anlamının genişlemesidir unsafe: unsafe bir kuruluşta bu kuruluşa yönelik denetim yükümlülüğünü yerelleştirirken unsafe , bir imzada bu yükümlülüğü arayan kişi için genişletir.

Bu, C# kullanıcı tabanının belirli kesimleri için büyük olasılıkla büyük bir hataya neden olan değişikliktir. Umudumuz, kullanıcılarımızın çoğu için bunun etkili bir şekilde şeffaf olması ve yeni kurallara güncelleştirmenin sorunsuz olmasıdır. Ancak, yansımanın büyük bölümleri gibi bazı büyük API yüzeylerinin işaretlenmesi unsafegerekebileceği göz önünde bulundurulduğunda, ekosistemi tamamen ikiye ayırmaktan kaçınmak için yeni kurallara uygun bir rampa olması gerekeceğini düşünüyoruz.

Kritik değişiklikler

Bu dil özelliğini uygulayan bir derleyiciye güncelleştirme sırasında aşağıdaki hataya neden olan değişiklikler gözlemlenebilir.

  • Güncelleştirilmiş bellek güvenliği kuralları etkinse (gelecekteki bir .NET sürümünde varsayılan ve hatta tek seçenek olabilir):
    • unsafe bir üyede artık bunu gerekli-güvenli değil olarak işaretler, yani arayanların bir unsafe bağlamda olması gerekir ve temel üye güvenliyse geçersiz kılmalar olamaz unsafe .
    • unsafebir üye veya tür otomatik olarak bir unsafe bağlam sunmaz, yani üye gövdelerinde ve başlatıcılarda işlemler etrafında unsafe açık unsafe blokların kullanılması gerekir.
    • extern açık düzendeki üyeler ve alanlar için açık unsafe/safe bir anahtar sözcüğünü seçin.
    • stackalloc belirli koşullar altında bir unsafe bağlam gerektirir.
    • unsafe değiştiricisi, hiçbir etkisi olmadığından tür bildirimleri, statik oluşturucular ve yıkıcılar üzerinde bir hatadır.
  • Yeni bir langversion altında:
    • Lambda çıkarımı daha fazla adayı dikkate alarak aşırı yükleme çözümleme belirsizliğine neden olabilir.
    • safe artık bunu tür olarak kullanan kodu kesebilecek bağlamsal bir anahtar sözcüktür.
    • Eski mod altında uyumluluk modu daha fazla hataya neden olabilir.

Ayrıntılı Tasarım

Terminoloji: Üyeleri çağırmak için gerekli-güvenli değil (daha önce arayan-güvensiz olarak bilinirdi)

Sözdizimi

Bu teklif şunu tanıtır:

Bu yeni söz dizimi yeni LangVersion altında kullanılabilir, ancak kabul etsek de, kabul ettiğinizde yapmanız gereken her şeyi kabul etmeden önce yapmanız gereken her şeyi yapmanız için şirket içinde yapmaya çalıştığımız şirket içinde kullanılabilir.

safe anahtar sözcük

Değiştirici, safe bunları gerekli-güvenli değil olarak işaretlemeye olanak unsafe tanıyan tüm bildirimlere uygulanabilir. Aynı bildirimde hem hem de unsafe değiştiricisinin safe uygulanmasına izin verilmez. ve değiştiricileri için unsafe geçerli olan diğer kısıtlamalar, Güvenli olmayan değiştiriciler ve bağlamlar bölümünde belirtilir.safe

Derleyici, açık safedüzendeki üyeler ve alanlar üzerinde extern açık veya unsafe değiştirici gerektirir. safe Gerekli olmadığı (ve bu nedenle derleyici için hiçbir etkisi olmayan) bildirimlerde bile izin verilmesi, kaynak oluşturucular (örneğin, LibraryImport) tarafından motive edilir.

Değiştiricisafe, bildirimi yalnızca gerekli-güvenlideğil olarak işaretler, güvenli bir bağlam sunmaz. Ayrıca blok veya ifade formu da yoktur safe .

Mevcut unsafe kurallar

Mevcut C# belirtiminde : unsafe ayrılmış büyük bir bölüm vardır. Geçerli bir C# derleyicisinin özelliği desteklemesi unsafe gerekmediğinden koşullu normatif olarak tanımlanır. şu anda koşullu normatif olarak kabul edilenlerin çoğu artık bu değişiklik sonrasında öyle olmayacaktır, işaretçi tanımının çoğu artık kendi içinde güvenli değil olarak kabul edilmez. İşaretçi türleri, Sabit ve taşınabilir değişkenler, tüm işaretçi ifadeleri (işaretçi dolaylı,işaretçi üyesi erişimi ve işaretçi öğesi erişimi hariç) ve deyiminin fixed tümü artık olarak kabul edilmez unsafeve bağlam unsafe içinde kullanılması gerekmeyen normal C# dilinde bulunur. Benzer şekilde, sabit boyutlu bir arabellek veya başlatılan stackalloc bir bildirim de güvenli C# içinde mükemmel yasaldır. Tüm bu durumlarda, yalnızca güvenli olmayan belleğe erişiyor .

Önemli olan, bu işaretçi gevşemesi, bir derlemenin güncelleştirilmiş bellek güvenliği kurallarınıkabul edip etmediğinden bağımsız olarak geçerlidir. Yalnızca gerçekten başvurulmayan veya doğrudan işaret edilen belleğe erişen işlemler bağlam unsafe gerektirmeye devam ediyor. Bu, artımlı geçiş yolunu etkinleştirir: Kullanıcılar, üye riski dahili olarak işlediğinde içeri veya arayan kişi denetime katılması gerektiğinde derleme genelinde kabul etme anahtarını çevirmeden önce içeri doğru hareket unsafe ederek mevcut kodu yeniden şekillendirebilir.

Bu değişiklikte bulunan hem kod bölümünün hem de unsafe diğer bölümlerin C# belirtiminin kapsamlı yeniden yazılması göz önünde bulundurulduğunda, teknik belirtimin mevcut kurallarının satır satır farkını sağlamak büyük olasılıkla kullanışlı olmayacaktır. Bunun yerine, belirli bir bölümde yapılacak değişikliğe genel bir bakış ve bağlamlarda unsafe izin verilenler için belirli yeni kurallar sağlayacağız.

Güvenli olmayan bağlamlar gerektiren ifadeleri yeniden tanımlama

Aşağıdaki ifadeler kullanıldığında bir unsafe bağlam gerektirir:

Bu ifadelere ek olarak, ifadeler ve deyimler olarak unsafeişaretlenmiş herhangi bir simgeye bağımlıysa koşullu olarak bir unsafe bağlam da gerektirebilir. Örneğin, gerekli-güvenli olmayan bir yöntemin çağrılması , invocation_expression bağlam unsafe gerektirmesine neden olur. Eklenen çağrıları (s, usingve benzeri) foreachiçeren deyimler, unsafe bir üye kullanırken bağlam da gerektirebilir.

Bu belgede "güvenli olmayan bir bağlam gerektirir" veya benzeri dediğimizde, bu, yapı için bir bağlam kullanılması gerektiğini belirten bir unsafe hata yaymak anlamına gelir.

Note

Bu bölümün büyük olasılıkla her ifadenin ve deyimin bağlam unsafe gerektirmesi için neleri dikkate alması gerektiğini resmi olarak bildirmek için genişlemesi gerekir.

İşaretçi türleri

Belirtildiği gibi, işaretçiler artık doğal olarak güvenli olmaz. §24.3'teki güvenli olmayan bağlamlara yapılan tüm başvurular silinir. İşaretçi türleri normal C# dilinde bulunur ve bunların varoluşuna getirilmesi gerekmez unsafe . Tür tanımları , diğer türlerde olduğu gibi §8.1 ve aşağıdaki bölümlerinde çalışılmalıdır.

Benzer şekilde, bağlamlara başvurular kaldırılmış şekilde işaretçi dönüştürmeleri§10'daunsafe çalışılmalıdır.

Benzer şekilde, işaretçi dolaylı işlemi, işaretçi üyesi erişimi ve işaretçi öğesi erişimi dışında işaretçiifadeleride bağlamlara başvurular unsafe kaldırılarak §12 içinde çalışılmalıdır. Bu ifadelerin anlamı hakkında hiçbir semantik değişmez; tek değişiklik, artık kullanmak için bir unsafe bağlam gerektirmemeleridir.

İşaretçi dolaylısı, işaretçi üyesi erişimi ve işaretçi öğesi erişimi için, çalışma zamanı yönetilmeyen bu erişim belleği olarak bu işleçler güvenli değildir. Bunlar §24 içinde kalır ve kullanılacak bir unsafe bağlam gerektirmeye devam ederler. Bağlam dışındaki unsafe tüm kullanımlar bir hatadır. Bu işleçlerle ilgili hiçbir semantik değişmez; hala bugün yaptıkları şeyin aynısını ifade etmeye devam ediyorlar. Bu ifadeler her zaman bir unsafe bağlamda gerçekleşmelidir.

Sabit deyim , bağlamlara başvurular kaldırılarak §13'eunsafe taşınır.

İşlev işaretçileri henüz ana C# belirtimine dahil değildir, ancak benzer şekilde etkilenir; işlev işaretçisi çağırması dışında her şey standart belirtime taşınır. İşlev işaretçisi çağırma ifadesi her zaman bir unsafe bağlamda gerçekleşmelidir.

Sabit boyutlu arabellekler

Sabit boyutlu arabelleklerin hikayesi işaretçilere benzer. Sabit boyutlu arabellek tanımı kendisi tehlikeli değildir ve §16.3'e taşınır. İfadenin primary_expressionelement_accessolarak gerçekleşmediği sürece, bir ifadedeki sabit boyutlu arabelleğe erişmek benzer şekilde güvenlidir; bunlar yukarıdaki kurallara göre güvenli olmayan bir pointer_element_access olarak değerlendirilir.

Yığın tahsisi

Yığın ayırmanın hikayesi de işaretçilere çok benzer. bir stackalloc işaretçiye dönüştürmek artık güvenli değildir; bu, bu işaretçinin güvenli olmayan ertelemesidir. Ancak yeni bir kural ekleyeceğiz:

Aşağıdaki deyimlerin tümü doğruysa bir stackalloc_expression güvenli değildir:

  • stackalloc_expression veya Span<T>öğesine ReadOnlySpan<T> dönüştürülüyor.
  • stackalloc_expression bir stackalloc_initializer yok.
  • stackalloc_expression, uygulanmış bir üye SkipLocalsInitAttribute içinde kullanılır.

Bu bağlamlarda, sonuçta elde edilen yığın alanı bilinmeyen bellek içeriğine sahip olabilir ve yönetilmeyen bellek erişimi çevresinde güvenli bir sarmalayıcı sağlayan bir türe dönüştürülür. Bu, ve Span<T>sözleşmesini ReadOnlySpan<T> ihlal eder ve bu nedenle bu kodun yazarı ve gözden geçirenleri tarafından ek incelemeye tabi olmalıdır.

Gevşetme olan kurallarda unsafe yapılan diğer değişikliklerden farklı olarak, bu bir sıkılaştırmadır ve bu nedenle bir molayı önlemek için yalnızca güncelleştirilmiş bellek güvenliği kurallarına katılım kapsamında geçerlidir.

Note

Bu, bağlama bakılmaksızın işaretçiye atamanın stackallocher zaman güvenli olduğu anlamına gelir.

Yönetilen türlerden birinin stackalloc hata olarak kaldığını unutmayın.

dil belirtiminde işaretçilerin hayatta kalmadığını stackallocawaitaçıkça belirtmemiz gerekebilir. Bu özellik öncesinde, işaretçilere yöntemlerde async izin verilmediğinden, bu daha önce gözlemlenemezdi.

awaitbağlamlarda unsafe

await bağlamlarda unsafe ifadelere izin verilir. Bu, zaman uyumsuz bir yöntemin önce beklenebilir öğeyi geçici bir değişkene dökmeden güvenli olmayan bir işlemi doğrudan beklemesine izin verir:

async Task M()
{
    unsafe
    {
        await DoUnsafeAsync();
    }
}

unsafe Task DoUnsafeAsync() => ...;

Bağlam unsafe hala programcının el ile güvenlik denetim sınırını temsil eder. bir işaretçi, işlev işaretçisi veya başka bir awaitgüvenli olmayan değer üzerinde canlı tutulursa, dil yeniden başlatıldıktan sonra kullanımın güvenli olduğunu kanıtlamaya çalışmaz; bu sorumluluk kod yazarında unsafe kalır. Bu, işaretçileri alanlarda depolama veya başvuranın ömrü sona erdikten sonra işaretçileri kullanma gibi işlemlere izin veren güvenli olmayan kodun geri kalanıyla tutarlıdır.

Ancak, await deyiminin fixed içinde izin verilmez. Deyimi fixed , deyiminin süresi boyunca taşınabilir bir değişkeni sabitler ve deyimi etkinken askıya alma izni vermek, sabitleme ömrünün zaman uyumsuz bir askıya alma noktasına yayılmasını gerektirir.

karşılık gelen csharpstandard güncelleştirmesi §15.15.1 Zaman Uyumsuz genel işlevlerindedir>. Bu bölüm boyunca , üstü çizili , metnin mevcut belirtimden kaldırıldığını, kalın ise metnin eklendiğini gösterir.

Bir zaman uyumsuz işlevin resmi parametre listesinin herhangi bir , outveya parametresini ya da ref bir türdeki herhangi bir parametreyi belirtmesi inderleme ref struct zamanı hatasıdır.

Güvenli olmayan bir bağlam (§24.2) için bir ifade (§12.9.8) veya bir deyim (§13.15) içermesi await derleme yield return zamanı hatasıdır.

Bir deyiminin (§24.7) bir ifade (§12.9.8) içermesi derleme await zamanı hatasıdır.fixed

sizeof

Önceden tanımlanmış belirli türler sizeof için her zaman sabit ve güvenlidir (§12.8.19) ve değişmeden kalır. Diğer türlerde, sizeof güvenli olmayan bağlam (§24.6.9) gerektirmek için kullanılır, ancak güncelleştirilmiş bellek güvenliği kurallarından bağımsız olarak artık güvenlidir.

Geçersiz kılma, devralma ve uygulama

Arayanlar temel tanımı kullanıyor olabileceğinden ve türetilmiş bir uygulama tarafından herhangi bir ekleme unsafe göremediğinden, üyenin başlangıçta güvenli olmayan herhangi bir geçersiz kılma veya uygulamasında üye düzeyinde eklenmesi unsafe bellek güvenliği hatasıdır.

Temsilciler ve lambda ifadeleri

Gerekli olmayan bir üyeyi bağlam dışında bir temsilci türüne dönüştürmek bellek güvenliği hatasıdırunsafe. Temsilci türleri ve işlev türlerigerekli-güvenli olamaz. Lambda simgesine uygulamak unsafe bir derleme zamanı hatasıdır.

extern

Yöntemler çalışma zamanı tarafından garanti edilemeyen yerel konumlar olduğundan extern , derleyici bunların güvenli mi yoksa güvenli olmayan mı olduğunu anlayamaz. Yöntem için kullanılan çağırma kuralı kullanıcı tarafından yanlış belirtilebileceği ve gözden geçirilerek el ile doğrulanması gerektiğinden, yalnızca değere göre parametre alan unmanaged yöntemler bile C# tarafından güvenli bir şekilde çağrılamaz.

Bu nedenle, güncelleştirilmiş bellek güvenliği kuralları altında, derleyici her extern yöntemin açıkça veya unsafeolarak safe işaretlenmesini gerektirir.

extern eski bellek güvenliği kurallarını kullanan derlemelerden yöntemler, genel yüzeyin parçası olmayan uygulama ayrıntıları olarak kabul edildiğinden örtük olarak unsafeextern kabul edilmez. extern başvuru derlemelerinde korunacağı garanti edilmiyor.

bunun eski kural derlemeleri için de geçerli olan uyumluluk modundan farklı olduğunu unutmayın çünkü imzada işaretçileri olan yöntemlerin çağrı sitesinde her zaman güvenli olmayan bir bağlama ihtiyacı olacaktır.

Güvenli olmayan değiştiriciler ve bağlamlar

Bugün, güvenli olmayan bağlam belirtiminin kapsamında olduğu gibi, unsafe sözcük temelli bir şekilde davranır, bloğun içerdiği unsafe metin gövdesinin tamamını bir unsafe bağlam olarak işaretler (yineleyici gövdeler hariç) ve bildirim durumunda çevresindeki bazı bağlamlar:

class A : Attribute
{
    public A(object o) { }
}
class C
{
    [A(default(int*[]))] void M1() { } // error: using pointers outside `unsafe` context
    [A(default(int*[]))] unsafe void M2() { } // ok
}

Güncelleştirilmiş bellek güvenliği kurallarını kabul ederek, unsafe bir üyede bunu gerekli-güvensiz olarak işaretler, denetim yükümlülüğünü çağırana genişletir ve bir bağlam unsafe (bunun yerine, yalnızca gövdedeki açık unsafe bölgeler bağlamlar oluştururunsafe).

unsafe artık bir anlamı olmadığından aşağıdaki bildirimlerde bir hata oluşur:

  • delegate,
  • statik oluşturucu,
  • Yıkıcı
  • tür bildirimi (class, structvb.).

unsafe bir oluşturucuda başlatıcı içinde bir unsafe bağlam tanıtır; örneğin, bir unsafe oluşturucu bir requires-unsafebase veya this oluşturucuyu çağırabilir.

Parametresiz , güvenli olmayan oluşturuculara sahip türler kısıtlamayı new() karşılamaz. Benzer şekilde ve buna ek olarak, parametresiz requires-unsafe oluşturucuları olan yapılar kısıtlamayı struct karşılamaz.

unsafe / safe bir üyenin içindeki iç içe anonim veya yerel işlevlere uygulanmaz . Aynı durum, bir unsafe bloğun içinde bildirilen anonim ve yerel işlevler için de geçerli olur (bunlar her zaman olduğu gibi bir unsafe bağlamdadır, ancak gerekli-güvensiz olmaz). Bir yerel işlevi gerekli-güvenli değil olarak işaretlemek için, el ile olarak unsafeişaretlenmesi gerekir. Lambdalar gerekli-güvenli değil olarak işaretlenemez (anahtar sözcüğüne unsafe izin verilmez).

Bir üye olduğunda partial, her iki bölüm de bugün C# kurallarından farklı olarak değiştirici üzerinde unsafe/safe anlaşmalıdır.

partial class C1
{
    public partial void M1(); // Error: both parts must be unsafe, or neither can be
    public partial unsafe void M2();
}

partial class C1
{
    public unsafe partial void M1() => Console.WriteLine("hello world");
    public partial void M2() => Console.WriteLine("hello world"); // Error: both parts must be unsafe, or neither can be
}

Özellikler get için ve/initseterişimciler bağımsız olarak olarak unsafe/safebildirilebilir. Erişimciler değiştiriciye unsafe/safe sahip değilse, özelliğinden devralır. Değiştiriciye benzer şekilde readonly , özellikler ve erişimciler için aşağıdaki kısıtlamaları kullanıma sunacağız:

  • unsafe / safe değiştiricileri her ikisi de değil, özelliğine veya erişimcilerine uygulanabilir.
  • Aynı değiştiriciyi unsafe/safe tüm özellik erişimcilerine uygulamak bir hatadır (bunun yerine bu değiştirici özelliğine uygulanmalıdır).

Olay erişimcilerine herhangi bir değiştirici yerleştirmek şu anda mümkün değildir ve bu teklif bunu değiştirmez, yani addremove ve olay erişimcileri bağımsız olarak olarak unsafebildirilemez. Yalnızca olayın tamamı olarak unsafeişaretlenirse, erişimcilerin güvenli olmadığını, aksi takdirde güvenli oldukları anlamına gelir.

Fields

unsafe bir alanda da bunu gerekli-güvensiz olarak işaretler ve başlatıcısına bağlam unsafe eklemez. Uyumluluk modu alanlar için de geçerlidir.

Bir özelliğin veya olayın olarak unsafe işaretlenmesi, yedekleme alanının güvenli olmamasını gerektirmez.

veya olan [StructLayout(LayoutKind.Explicit)] bir türde tüm örnek alanları veya [ExtendedLayout]olarak işaretlenmelidirsafe.unsafe Alan bir otomatik özelliğin veya alan benzeri olayın arkasında "gizli" ise, safe/unsafe gereksinim bunun yerine otomatik özelliğe veya alan benzeri olaya taşınır.

Meta veriler

Derleme yeni bellek güvenliği kurallarıyla derlendiğinde ile işaretlenir MemorySafetyRulesAttribute (aşağıda ayrıntılı olarak gösterilmiştir), ile dil sürümü olarak doldurulur 15 . Bu, tüm aşağı akış tüketicilerine, derlemede tanımlanan tüm üyeleri çağırmak için bir bağlam gerekiyorsa uygun şekilde ilişkilendirileceğine RequiresUnsafeAttribute dair bir unsafe sinyaldir (aşağıda ayrıntılı olarak açıklanmıştır). Böyle bir derlemede ile işaretlenmemiş RequiresUnsafeAttribute herhangi bir unsafe üye, üyenin imzasında yer alan türlerden bağımsız olarak bir bağlamın çağrılması gerekmez.

kaynaktaki MemorySafetyRulesAttribute herhangi bir simgeye veya RequiresUnsafeAttribute açıkça uygulanması bir hatadır.

Derleyici, eski bellek güvenliği kurallarını kullanan derlemelerdeki -işaretli üyeleri yoksayar RequiresUnsafeAttribute(bunun yerine, uyumluluk modu orada kullanılır).

Türü olmayan bir üye güvenli olmadığında, derleyici meta verilerde üye üzerinde bir RequiresUnsafeAttribute uygulama sentezler.

MemorySafetyRulesAttribute ve RequiresUnsafeAttribute tanımı, standart iyi bilinen üye kuralları için gerekirse derleyici tarafından sentez edilir.

namespace System.Runtime.CompilerServices
{
    /// <summary>Indicates the language version of the memory safety rules used when the module was compiled.</summary>
    [AttributeUsage(AttributeTargets.Module, Inherited = false)]
    public sealed class MemorySafetyRulesAttribute : Attribute
    {
        /// <summary>Initializes a new instance of the <see cref="MemorySafetyRulesAttribute"/> class.</summary>
        /// <param name="version">The language version of the memory safety rules used when the module was compiled.</param>
        public MemorySafetyRulesAttribute(int version) => Version = version;
 
        /// <summary>Gets the language version of the memory safety rules used when the module was compiled.</summary>
        public int Version { get; }
    }

    [AttributeUsage(AttributeTargets.Event | AttributeTargets.Method | AttributeTargets.Property | AttributeTargets.Constructor, AllowMultiple = false, Inherited = false)]
    public sealed class RequiresUnsafeAttribute : Attribute
    {
    }
}

Uyumluluk modu

Uyumlu amaçlarla ve yeni kuralları etkinleştirirken oluşan hatalı negatiflerin sayısını azaltmak için, yeni kurallara güncelleştirilmemiş modüller için bir geri dönüş kuralımız vardır. Bu tür modüller için, üye parametre türleri veya dönüş türü arasında bir yerde işaretçi veya işlev işaretçisi türü içeriyorsa (örneğin, işaretçi olmayan bir türe iç içe yerleştirilmiş olabilir) int*[] olarak kabul edilir. Bunun, daha önce çağrı sitelerinde güvenli olmayan bağlama gerek duymadığından kısıtlama türlerindeki (örneğin, where T : I<int*[]>) işaretçiler için geçerli olmadığını unutmayın.

Hedef üyenin herhangi bir şey için bu işaretçi türünü kullanmasının tür açısından güvenli bir yolu olmadığından, bu, değiştirilen genel parametreleri (örneğin, yerine kullanıldığında yöntemi I<T>.M(T)Tint*[]) içermez.

Bu tür uyumluluk modu , güvenli olmayan üyelerin güncelleştirilmiş bellek güvenliği kurallarını kabul etmeyen arayanlardan bile bir bağlam kullanılmasını gerektirir unsafe . Bu, yalnızca LangVersion'ı güncelleştirmenin (ancak bellek güvenliği kuralları sürümünü güncelleştirmemesinin) çoğu işaretçi işlemini güvenli hale getirdiği (güncelleştirilmiş kurallara kabul edildiğinde büyük olasılıkla gerekli-güvenli değil olarak işaretlenecek işaretçilere sahip işlevleri çağırma dahil) ve dolayısıyla bu geçiş penceresinde kodun daha az korunduğu bir "dip" yapmaktan kaçınmalıdır.

VB

Bugün VB'de bağlam olmadığından ve işaretçilerle orada çalışma imkanı olmadığından, unsafe üyeler için Visual Basic desteği eklememiz gerekmez.

unsafe ifadeler

İfade unsafe , tek bir ifadeyi değerlendirmek için en düşük unsafe bağlamı tanıtır. Bir bloğun kapsamı gereksiz yere genişlettiği unsafeunsafe veya blokların söz dizimsel olarak kullanılamadığı unsafe durumlarda (filtreler, alan başlatıcılar ve oluşturucu başlatıcılar gibi catch ) yararlıdır.

Sözdizimi

unsafe_expression primary_no_array_creation_expression olarak eklenir:

unsafe_expression
    : 'unsafe' '(' expression ')'
    ;

Başka bir yerde anahtar sözcükle aynı AllowUnsafeBlocks gereksinime unsafe tabidir.

Anlambilim

birunsafe_expression, unsafe değerlendirmek için bir bağlam oluşturur. Bu, kapalı ifade içinde işaretçi başvurularının, işlev işaretçisi çağrılarının ve requires-unsafe üyelerine yönelik çağrıların tümüne izin verilen anlamına gelir. türü ve değeri unsafe_expression , kapalı ifadenin türü ve değeridir.

unsafe tarafından unsafe_expression oluşturulan bağlam, kapanış parantezinin ötesine uzanmıyor.

Motivasyon ve geçiş örnekleri

Bazı söz dizimsel konumlar blokları kabul unsafe etmese de, güvenli olmayan üyeleri çağıran alt ifadeler içerebilir. İfadeler olmadan unsafe , bu tür kodun geçirilmesi için güvenli olmayan alt ifadenin yardımcı bir yerel işleve veya geçici bir değişkene ayıklanması ya da gerektiğinden daha unsafe geniş bir blok kullanılması gerekir; bu da amacı gizler ve ayrıntı düzeyini artırır.

await bir requires-unsafe yönteminde. Beklenen yöntem gerekli-güvensiz hale geldiğinde, tüm awaitçevresinde bir unsafe blok kullanılabilir, ancak bir unsafe ifade güvenli olmayan bağlamı gerektiren işlemle sınırlı tutabilir:

// Without unsafe expressions: the unsafe context includes the await expression
unsafe
{
    // SAFETY: Discharges obligations because reasons
    await DoWork();
}

// With unsafe expressions: the unsafe context wraps only the call
// SAFETY: Discharges obligations because reasons
await unsafe(DoWork());

Filtreleri yakalayın. Yan when tümcesinin catch filtresi bir ifadedir, deyim gövdesi değildir. Bir unsafe blok yalnızca deyimleri çevreleyebilir, bu nedenle yalnızca filtre ifadesinin çevresine yerleştirebileceğiniz bir yer yoktur. Filtrede kullanılan bir yöntem requires-unsafe olduğunda, ifade içermeyen unsafe tek alternatif yardımcıdır:

// Without unsafe expressions: must spill to a local function
static bool FilterHelper(Exception e)
{
    // SAFETY: Discharges obligations because reasons
    unsafe { return NowUnsafeCall(e); }
}

try
{
    await DoWork(); // 'await' here prevents wrapping the whole try/catch in 'unsafe'
}
catch (Exception e) when (NowUnsafeCall(e))
{
}

// With unsafe expressions: inline and minimal scope
try
{
    await DoWork();
}
// SAFETY: Discharges obligations because reasons
catch (Exception e) when (unsafe(NowUnsafeCall(e)))
{
}

Alan başlatıcıları. Güncelleştirilmiş kurallar altında, unsafe bir alanda başlatıcısında bir unsafe bağlam tanıtılmaz. Alanın başlatıcısı bir requires-unsafe üyesini çağırdığında, bir unsafe ifade yardımcı yöntemi gerektirmeden bağlamı sağlar:

// Without unsafe expressions: must spill to a helper method
static int InitialValue()
{
    // SAFETY: Discharges obligations because reasons
    unsafe { return ReadFromPointer(); }
}
static int _value = InitialValue();

// With unsafe expressions: inline
// SAFETY: Discharges obligations because reasons
static int _value = unsafe(ReadFromPointer());

Oluşturucu başlatıcıları. this(...) ve base(...) başlatıcı bağımsız değişken listeleri deyim gövdeleri değil ifadelerdir. Bu bağımsız değişkenlerden biri requires-unsafe üyesini çağırırsa, yine bir unsafe blok eklemek için yer yoktur, bu nedenle çağrının aksi takdirde bir yardımcıya taşınması gerekir:

class C(int x)
{
    // SAFETY: Discharges obligations because reasons
    C() : this(unsafe(GetUnsafeValue()))
    {
    }
}

class Derived : Base
{
    // SAFETY: Discharges obligations because reasons
    Derived() : base(unsafe(GetUnsafeValue()))
    {
    }
}

Satır içi aramalar. Daha genel olarak, yalnızca gerekli-güvenli olmayan alt ifadenin sarmalanması denetim kapsamını sıkı tutar ve çevresindeki güvenli kodun (bağımsız değişken değerlendirmesi veya içeren yöntem çağrısı gibi) bağlama çekilmesini unsafe önler:

extern int Add(int i1, int i2); // Some fancy extern addition function

// Code I want to write:

// SAFETY: Discharges obligations because reasons
Console.WriteLine(unsafe(Add(1, 2)));

// Code I have to write without unsafe expressions, option 1
// (unsafe context unnecessarily includes the WriteLine call):

// SAFETY: Discharges obligations because reasons
unsafe
{
    Console.WriteLine(Add(1, 2));
}

// Code I have to write without unsafe expressions, option 2
// (very verbose and harder to read):
int result;
// SAFETY: Discharges obligations because reasons
unsafe
{
    result = Add(1, 2);
}
Console.WriteLine(result);

Sorular

(yanıtlandı) RequiresUnsafeAttribute üyeleri belirtmek için kullanın

Üyede anahtar sözcüğünü unsafe kullanarak gerekli olmayan üyeleri belirtmek yerine, üyeye uygulanan bir özniteliği (RequiresUnsafeAttribute) kullanabiliriz (ve üyeler üzerindeki değiştiricinin unsafe anlamını değiştiremeziz).

Avantajları unsafe:

  • diğer dillere benzer ve bu nedenle anlaşılması daha kolay,
  • bir öznitelikten daha bulunabilir.

Özniteliğin (veya başka bir anahtar sözcüğün) avantajları:

  • , olarak unsafeişaretlenmiş mevcut üyelerin hataya neden olmamasını önler.
  • artımlı benimseme mümkündür (üyeye göre),
  • tüm vücudu olarak unsafe işaretlemeye zorlamaz (anahtar sözcükle unsafe bile gövdeler üzerinde bir etkisi olmayacak şekilde değiştirebilirizunsafe ),
  • , üyenin kendisini requires-unsafeolarak işaretlemeye gerek kalmadan tüm güvenli olmayan hataların gizlenmesini sağlar (örnekler).

Tartışma:

Yanıt: unsafe üyeleri belirtmek için anahtar sözcüğü kullanın.

Yerel işlevler/lambda güvenli bağlamları

Şu anda unsafe bir yöntem gövdesinin kapsamı sözcük temelli olarak belirlenmiştir. İç içe yerel işlevler veya lambdalar bunu devralır ve gövdeleri bellekte güvenli olmayan bir bağlamdadır. Bu davranışı dilde saklamak mı istiyoruz? Çağıranın güvenli olması gerektiğini göstermek için kullanılan değiştirici olarak tutarsak unsafe , bunun yöntemin imzasını etkileyebileceğinden unutmayın. Şu anda önerilen şekilde, iç içe anonim ve yerel işlevler, içerdikleri üyenin güvenli olmayan bağlamını korumaz.

Temsilci türü unsafety

Temsilci türlerinin ve lambdaların (ve işlev türlerinin) gerekli-güvenli değil olarak işaretlenmesine izin verebiliriz. Bunun için birkaç ek kural (dış bağlam) unsafe gerekir:

  • tür bağımsız değişkenleri olarak güvenli olmayan temsilciler kullanılmasına izin verme,
  • bu temsilcileri güvenli olmayan (Delegateve Expression) object herhangi bir şeye dönüştürmeye izin verme, bu olmadan, yanlış noktada ek açıklamaları zorlama unsafe ve ty'nin gerçek alanının düzgün şekilde çağrılmadığı bir alana unsafesahip olma riski vardır.

Lambda/yöntem grubu güvenli temsilci türlerine dönüştürme

Lambdalara ve temsilcilere izin unsafe verirsek, gerekli olmayan bir lambda veya yöntem grubunun bir bağlamda uyarı veya hata olmadan izin verilen gerekli olmayan güvenli olmayan bir unsafe temsilci türüne dönüştürülmesi gerekir mi? Bunu yapmazsak, özellikle LINQ sorguları aracılığıyla geçirilen tüm numaralandırılabilir öğeler olmak üzere ekosistemin çeşitli bölümleri için oldukça acı verici olabilir.

Lambda/yöntem grubu doğal türleri

Günümüzde semantik ve kod oluşturma (ek meta verilerin yanı sıra) üzerindeki tek gerçek etki, imzada olduğunda lambda veya yöntem grubunun unsafe değiştirmektir. Bunu yapmaktan kaçınsaydık, her ikisinde de gerçek bir etkisi olmazdı, bu da benimseyenlere davranışın alttan altta değişmediğine dair daha fazla güven verebilirdi.

Note

Lambdalara sahip unsafe olma özelliğini kullanmaya karar verirsek bu teklifi, lambdaların ilk etapta bildirilmesine unsafe izin verecek bir söz dizimi değişikliği içerecek şekilde güncelleştirmemiz gerekir.

Yönetilen türlerin işaretçileri

C# 11 , bir uyarıyla yönetilen türlere yönelik işaretçilere izin verir. İşlemlerin adresi için bu uyarıyı gevşetmeli miyiz? Sorunun yalnızca kullanıcının normal güvenli olmayan evrim kuralları altında yer alan bu tür bir işaretçiye başvurmaması olduğunu düşünüyoruz. Peki ya?sizeof

stackalloc başlatıldı olarak

Bugün, belirtim her zaman belleği başlatılmamış olarak kabul eder stackalloc ve el ile temizlenmediği veya atanmadığı sürece içeriğin tanımsız olduğunu söyler. Bunu belirtim hatası olarak mı değerlendiriyoruz yoksa dikkate aldığımız şeyi belirli amaçlarla unsafe mı değiştirmemiz stackalloc gerekiyor?

stackalloc Kural

LDM, yukarıda tanımlanan kuralı ve işaretçiyle ilgili diğer değişiklikler gibi kabul etmeden bağımsız olarak uygulanıp uygulanmayacağını onaylamalıdırstackalloc.

AllowUnsafeBlocks

AllowUnsafeBlocks anlamı şu anda değişmemiştir. veya trueanahtar sözcüğünü kullanabilmek unsafe için olarak ayarlanması SkipLocalsInitAttribute gerekir. BCL bu SkipLocalsInitAttribute özniteliği requires-unsafe olarak işaretleyebildiğinden güncelleştirilmiş kurallar altında bunu gerektirmemeli miyiz? Anahtar sözcük için safe de gerekli mi? Bunu hem bloklar hem unsafe de unsafe üye bildirimleri veya bunların diğer birleşimi için gerekli mi yapmalıyız?

(yanıtlandı) unsafe Ifa -de

Daha kapsamlı unsafe özelliklere sahip diğer diller, kullanıcı ergonomisini geliştiren ve yazarların kullanıldığı yeri unsafe daha hassas bir şekilde sınırlamasına olanak sağlayan bir ifade olarak eklenmiştirunsafe. Bu C# dilinde olmasını istediğimiz bir şey mi? Doğrudan güvenliği işleyen bir unsafe üyeye satır içi bir çağrı düşünün: Şu anda, yazarın deyimin tamamını bir unsafe blok içinde sarmalayıp bağlamın kapsamını genişletmesi unsafe veya iç işlev çağrısını ara değişkene ayırması gerekir.

extern int Add(int i1, int i2); // Some fancy extern addition function

// Code I want to write:
Console.WriteLine(unsafe(Add(1, 2)));

// Code I have to write option 1, unsafe context unnecessary includes the WriteLine call
unsafe
{
    Console.WriteLine(Add(1, 2));
}

// Code I have to write option 2, very verbose and harder to read:
int result;
unsafe
{
    result = Add(1, 2);
}
Console.WriteLine(result);

Cevap: Evet. Ayrıntılı tasarım bölümüne bakın.

(yanıtlandı) await bağlamlarda unsafe

Bağlamlardaki unsafe ifadelerle ilgili await kısıtlamaları gevşetmeli miyiz? Özellikle izin vermek await UnsafeMethod() yararlıdır, aksi takdirde kullanıcıların bunu öğesine yeniden yazması gerekir Task t; unsafe { t = UnsafeMethod(); } await t;. Daha fazla ayrıntı için bkz. yineleyicilerde/zaman uyumsuzda ref/unsafe .

Cevap: Evet. Bağlamlara unsafe bakınawait.

Diğer unsafe bağlamlar ve rahatlamalar

Bağlamları unsafe takip ederekawait, zaman uyumsuz ve yineleyici yöntemleri için bağlamlar unsafe ve işaretçi parametrelerine de izin yield vermeli miyiz? Daha fazla ayrıntı için bkz. yineleyicilerde/zaman uyumsuzda ref/unsafe .

Güvenli bağlamda da izin vermeli &UnsafeMethod miyiz? Teklifte olduğu gibi bugün, yöntem olarak unsafeişaretlenmişse bağlam gerekirunsafe. Ancak başvurulduğunda/çağrıldığında bağlama ihtiyaç duyacağımız unsafe adresini yeni aldığımızdan, güvenli bir bağlamda kendi adresinin ele alınmasına izin verebiliriz.

LangVersion'da güvenli olmayan rahatlamalar

LangVersion'da güvenli olmayan bağlam gevşetmelerini koşulsuz hale getirmeli miyiz?

  • LDM 2026-04-06: Bunlar bellek güvenliği kuralları sürümüne bağlı değildir
  • LangVersion ne olacak?

(yanıtlandı) unsafe türlerde

Bir türün sözcük temelli kapsamının unsafe tamamını otomatik olarak bağlam unsafe haline getirmeyi ve bir tür üzerinde unsafe bir için uyarı vermeyi düşünemedik, bunun hiçbir anlamı yoktu.

  • LDM 2025-11-12: unsafe bir tür üzerinde hiçbir anlamı olmayacaktır
  • LDM 2026-05-13: Bir hata olacaktır (geri bildirim temelinde yeniden ziyaret edilebilir)

Yanıt: unsafe Bir türdeki hata ( geri bildirim temelinde yeniden ziyaret edilebilir) güncelleştirilmiş kurallar altında.

(yanıtlandı) Uç durum senaryolarında güvenli olmayan hataları gizlemeye izin verme

Aşağıdaki senaryolarda gerekli-güvenli olmayan hataların gizlenmesi nasıl izin verilmelidir?

class A : System.Attribute
{
    unsafe public A() { } // declaring requires-unsafe constructor
}

class C
{
    [A] public void M() { } // error: applying requires-unsafe `A..ctor`
}

class B : A
{
    public B() { } // error: calling requires-unsafe `A..ctor` (implicit `: base()`)
}

class X<T> where T : new();
class D
{
    public void M(X<A> x) { } // error: using `X` which uses requires-unsafe `A..ctor`
}

Gerekli-güvenli olmayan hataları engellemek için, bir şekilde bu üyelerin imzasına bir unsafe bağlam eklememiz gerekir. unsafe Ancak anahtar sözcüğü unsafe bir bağlam sunmaz. İmzada bir unsafe bağlam tanıtabilirizunsafe, ancak bir temel gerekli olmayan oluşturucuyu çağırmak istediğinizde oluşturucuyu zorunlu kılmanın güvenliolmayan olması talihsiz görünüyor. İmza uyarılarında, kullanıcıların bu nadir uç durumlara ulaşmaları durumunda yerinde gizleyebilecekleri güvenli olmayan kullanımlar yapabiliriz.

Türlerde unsafe de bağlam tanıtılmadığındanunsafe benzer bir sorun vardır:

class A : Attribute
{
    unsafe public A() { } // declaring requires-unsafe constructor
}

[A] class C; // error: applying requires-unsafe `A..ctor`

class B() : A(); // error: calling requires-unsafe `A..ctor`

class X<T> where T : new();
class D : X<A>; // error: inheriting from `X` which uses requires-unsafe `A..ctor`
  • LDM 2026-05-13:
    • öznitelik uygulaması gibi yürütülemez kod, desteklenmeyen bir hata olarak kalmalıdır (bazı geri bildirimler duyana kadar)
    • gibi new() diğer uç durumlar da geri bildirim duyana kadar hata olarak kalabilir
    • parametresiz requires-unsafe oluşturucularının bildirilmesine izin verme
    • yalnızca bir unsafe oluşturucu başlatıcısı requires-unsafebase veya this oluşturucuyu çağırabilir

params koleksiyonları

class C
{
    unsafe public C() { } // declaring requires-unsafe constructor
}

class B
{
    public void M(params C c) { }
}

Güvenli olmayan unsafe koleksiyon çağrı sitesinde oluşturulacağından bu tür bir params yöntemi çağırmak bir bağlam gerektirdiğinden bu bildirime yalnızca izin verilebiliyordu. Bu, bildirimin etkili bir şekilde güvenli olmamasını gerektirse de, ek açıklamayı unsafe gerektirebilir veya en azından bu olgu hakkında uyarı verebiliriz. Koleksiyon örneğinin params , diğer benzer özelliklerin burada (Obsolete, UnmanagedCallersOnly) nasıl davrandığını gösteren bir hata olduğunu, ancak bunun bir uygulama hatası olabileceğini unutmayın.

İyi bilinen üyeler

Uygulamanın basitliği ve aklı başında olması için, derleyicinin tüm iyi bilinen üyelerin (örneğin Array.Length) güvenli olduğu varsayılabilir (yani , güvensiz olması gerekmez) varsayılmasını öneriyoruz.

Xml belgeleri

Arayanlara bir yükümlülüğün geçirilmesi, bu yükümlülüğün ne olduğunu açıkça belirtme sorumluluğuyla birlikte gelir. Xml belgelerinde zaten gösterilebileceklerin ötesinde bunu resmileştirmeli miyiz?

İşaretlenen unsafe üyelerin, çağıranın kodun doğru olduğundan emin olmak için ne yapması gerektiğini belirten açıklamaları olmalıdır. Belgelerde görmeyi ve ayırt edilmeyi kolaylaştırmak için yeni bir XML belge etiketi yararlı olabilir: <safety />. Tüm ön/son koşulların blok içine <safety> yerleştirilmesi beklenir.

Her unsafe bloğun muhakemesini belgeleyemek için // SAFETY benzer yorumların kullanılmasını öneririz. Bunları derleyici tarafından da denetlemeli miyiz (örneğin, varsayılan olmayan bir uyarınız mı var) yoksa bunu çözümleyiciye mi bırakmalıyız?

Daha anlamsız unsafe uyarılar

Daha fazla bildirim anlamsız unsafe uyarı veya hata üretmeli mi? Örneğin, boş gövdeli yöntemler (veya extern), vb. Yine de gereksiz unsafe bir IDE çözümleyicimiz zaten var.

Statik oluşturucuya benzer bir hata mı olmalıdır [ModuleInitializer] unsafe void M() { } ?

(yanıtlandı) unsafe Alanları

Bugün, bir alanda herhangi bir teklif yapılmaz unsafe . Ancak, olarak unsafe işaretlenmiş bir alandan okunan veya bu alana yazılan herhangi bir değerin bir unsafe bağlamda olması gerektiği şekilde eklememiz gerekebilir. Bu, aşağıdakiler gibi kodla ilgili endişelere daha iyi açıklama eklememizi sağlar:

class SafeWrapper
{
    internal byte* _p;

    public void DoStuff()
    {
        unsafe
        {
            // ... validate that the object state is good ...
            // ... perform operation with _p .... 
        }
    }
}

// Elsewhere in safe code:
void M(SafeWrapper w)
{
     w._p = stackalloc byte[10];
}

Otomatik özelliğin yedekleme alanını da olarak unsafeişaretlemeli miyiz?

Üyelerin kararıyla tutarlı olmak için, bir alanda bağlamı unsafe tanıtmamak unsafe da iyi olacaktır. Alanın başlatıcısında güvenli olmayan gerekli işlemler kullanılıyorsa, kullanıcı bunları her zaman bir yönteme kapsülleyebilir veya ifadeler ekleyebilirizunsafe.

  • LDM 2026-05-13: alanlar aracılığıyla requires-unsafeunsafeolarak işaretlenebilir; başlatıcılar bağlam içinde unsafe değildir.

(yanıtlandı) Açık düzen

Yapılardaki alanların olarak işaretlenmesi mi yoksa [StructLayout(Explicit)] olarak [ExtendedLayout] işaretlenmesi unsafemi gerekiyor?

Öneri: evet.

  • LDM 2026-05-13: evet, aynı s için unsafeolduğu safe gibi veya externgerektirir.

Açık düzen ve yedekleme alanları

Derleyici bir türdeki Explicit/Extended otomatik özellik veya alan benzeri bir olay için bir yedekleme alanını sentezlerse, bunun yerine özellik/olay gerekir safe/unsafe mi? Aksi takdirde, kullanıcı bu otomatik bildirimleri el ile alan artı sarmalayıcı üye bildirimlerine genişletmeye zorlanır. Peki ya bir yedekleme alanı alan birincil oluşturucu parametresi? Parametre bildiriminde hem hem safe de unsafe değiştiriciye şu anda izin verilmiyor.

Birleştirilmiş üyeler

Kullanıcı tarafından bildirilen bir üye requires-unsafe olarak işaretlendiğinde, anlatılamayan ancak yansıma (yineleyiciler için gibi MoveNext ) aracılığıyla çağrılabilen derleyici tarafından sentezlenen ilişkili üyeler de meta verilerde öznitelikle [RequiresUnsafe] işaretlenmeli mi?

Bunun özellik ve olay erişimcileri için geçerli olmadığını unutmayın; burada tam olarak ne zaman güvenli olmaları gerektiğinitam olarak belirtiriz ve bu nedenle özniteliği meta verilerde buna karşılık olarak alırlar.[RequiresUnsafe]

[Out] ve [SkipLocalsInit]

Örneğin VB, parametrelerin başlatılmasını [Out] garanti etmediğinden, ile birlikte [SkipLocalsInit]C# dilinde bu parametreleri çağırmak düşünülebilir unsafe . Öte yandan, arayanın sorunu sözleşmesini [Out] yerine getirmemesi gibi bir histir (benzer şekilde başka birçok yolla güvenli olmayabilir).

Bu durumların olması unsafegerektiğine karar verirsek, kabul ettiğimiz yöntemleri dışlayabiliriz (şu anda bunlar doğru kullanımını [Out] garanti eden C# dilindedir, ancak diğer diller yeni kuralları uygularsa, bunu da garanti etmelidirler).

Başlatılmamış bir değişkenin adresini alma

Bugün, kesinlikle atanmamış bir değişkenin adresini almak, bu değişkenin kesinlikle atandığını ve başlatılmamış üyeyi ortaya çıkardığını göz önünde bulundurabilir. Bunu çözmek için birkaç seçeneğimiz vardır:

  1. Üzerinde bir adres işlecinin kullanılmasına izin vermeden önce değişkenlerin kesinlikle atanmalarını gerekli kılmalıdır.
  2. Başlatılmamış bir değişkenin adresini güvenli olmayan hale getirin.

Examples:

static void SkipInit<T>(out T value)  
{
    // value is considered definitely assigned after the address-of
    fixed (void* ptr = &value);
}
int i;
// i is considered definitely assigned after the address-of
_ = &i;
// Incrementing whatever was on the stack
i++;

Değeri MemorySafetyRulesAttribute

"Enabled"/"updated" bellek güvenliği kuralları sürümü ne olmalıdır? 2? 15? 11? Ayrıca bkz. SDK Güvenli Olmayan Benimseme ve Daha aşamalı kabul etme?

(yanıtlandı) Daha aşamalı kabul mü?

Bugün kabul etmek size aynı anda iki şey sağlar: kendi kodunuzdaki güvenli olmayan kuralların uygulanması ve tüketicilerinize ek açıklamalarınızın kasıtlı olarak yayınlandığı bir sinyal (derleme düzeyinde bir öznitelik aracılığıyla) . Derlemelerine tam olarak açıklama eklediklerini yayımlamaya hazır olmadan açıklama eklerken zorlama tanılamalarını almaya başlamak isteyen kullanıcılar olabilir. Güvenli olmayan tanılamaları uyarı olarak ortaya çıkarabilecek bir "orta" kabul düzeyine sahip olmamız ve tam kabul etme işleminin hatalara neden olması gerekir mi?

(yanıtlandı) Daha ayrıntılı kabul etme

Kullanıcıların kaynak kodun belirli bölgeleri için özelliği etkinleştirmek üzere yönergeleri kullanabildiği, null atanabilir başvuru türleri için oluşturduğumuza benzer ayrıntılı, bölge tabanlı bir kabul mekanizması sağlamak mı? Ayrıca bkz . Kod bölgelerini kabul etme/kapatma.

(yanıtlandı) extern örtük olarak güvenli değil

Açık bir RequiresUnsafeAttribute anahtar sözcük olmadan sentezlenen tek yer unsafe şu anda burasıdır. Bu aykırı değerler bizim için sorun değil mi?

Ayrıca CoreLib bugün birçok extern yöntemini (FCalls) güvenli olarak kullanıma sunar. Extern yöntemlerini örtük olarak güvenli olmayan olarak ele almak için örtük olarak güvenli olmayan extern yöntemlerinin güvenli bir sarmalayıcı ile sarmalanması gerekir. Çalışma zamanı uygulama ayrıntıları nedeniyle ek sarmalayıcı eklemenin zor olduğu durumlarla karşılaşabiliriz.

  • LDM 2026-04-01: extern üyeler açıkça güvenli veya güvenli değil olarak işaretlenmelidir
  • LDM 2026-04-06: Aynı karar yinelenmiş
  • LDM 2026-04-13: anahtar sözcüğü kullanma safe geçici kararı
  • LDM 2026-05-13: anahtar sözcüğünü kullanın safe (yine de yeniden ziyaret için açık)

Yanıt: extern üyelerin ya unsafe da safe olarak işaretlenmesi gerekir (bunun için yeni bir anahtar sözcük ekleriz, ancak geri bildirime bağlı olarak özellik göndermeden önce bu anahtar sözcüğü yeniden ziyaret edebiliriz).

(yanıtlandı) Üye olmayanlardaextern izin ver safe (LibraryImport)

Bu soruyu ilk kez düşünmedik. Çalışma grubu başlangıçta ve extern üyelerini birlikte ele alırLibraryImport. LDM daha sonra, imzalarında blok veya işaretçi bulunan üyelerin unsafe açık safe bir işaret taşıması gerekip gerekmediğine karar verdi ve çözümlenebilir yöntem gövdelerinin bu törene ihtiyacı olmadığına karar verdi : bir extern sınırın aksine, güvenlik yükümlülüklerini yerine getirip getirmediklerini belirlemek için uygulamaları denetlenebilir. Sonuç olarak, safe şu anda yalnızca açık düzendeki extern üyeler ve alanlar gibi açık bir güvenlik seçiminin gerekli olduğu durumlarda izin verilir.

Kitaplık ekibi o zamandan beri kaynak oluşturmadanLibraryImport gelen ve sorunun yeniden gözden geçirilmesini gerektiren yeni veriler sağladı. LibraryImport P/Invoke'un tercih edilen modern biçimidir, ancak oluşturulan kısmi uygulamanın bir uygulama ayrıntısı extern olup olmadığıdır. Kesilebilir imza için oluşturucu doğrudan extern bir uygulama (basitleştirilmiş örnekler) yayabilir:

// User code
[LibraryImport("kernel32.dll")]
static partial int Blit(int x);

// Generated code
[DllImport("kernel32.dll", EntryPoint = "Blit", ExactSpelling = true)]
static extern partial int Blit(int x);

extern Oluşturulan bildirimin veya safeolarak işaretlenmesi unsafe ve kısmi bildirimlerin güvenlik değiştiricileri üzerinde anlaşmaya varması gerekir. Bu nedenle kullanıcı tarafından yazılan bildirimin aynı değiştiriciye ihtiyacı vardır.

Hazırlama gerektiren bir imza için, oluşturucu bunun yerine özel externbir çevresinde yönetilen sarmalayıcı yayar:

// User code
[LibraryImport("kernel32.dll")]
static partial int NotBlit(ref int x);

// Generated code
static partial int NotBlit(ref int x)
{
    fixed (int* xNative = &x)
    {
        return __PInvoke(xNative);
    }

    [DllImport("kernel32.dll", EntryPoint = "NotBlit", ExactSpelling = true)]
    static extern unsafe int __PInvoke(int* xNative);
}

Burada kullanıcıya yönelik kısmi yöntem değildir extern, bu nedenle safe izin verilmez. Bu nedenle kullanıcının kullanılabilir söz dizimi, oluşturucunun hangi uygulama şeklini seçtiğine bağlıdır, ancak bu seçenek sözleşmenin LibraryImport bir parçası değildir ve oluşturucu geliştikçe değişebilir. Her zaman bir sarmalayıcı oluşturmak söz dizimini tutarlı hale getirir, ancak doğrudan yayılabilir imzaları kötüleştirebilir ve ile LibraryImportarasında DllImport kullanıcı deneyimi farkı oluşturur.

Üye safe bile güvenli değil olarak işaretlenebilen her bildirimde unsafe izin verilmelidir? Alternatif olarak, dil kaynak oluşturucular tarafından uygulanan kısmi üyeler için daha dar bir kural sağlamalı mı?

Çalışma Grubu Önerisi: Bildirimi işaretleyebilecek safe herhangi bir yerde bildirim değiştiricisi olarak izin ver unsafe, güvenli değildir. Gerekli olmadığı durumlarda no-opsafe . Dilin gerekmediği durumlarda açık değiştirici gerektirmesi gereken senaryolar (sarmalayıcı olmayanLibraryImport bir oluşturucu oluşturan bir gibiextern) veya safevarlığını unsafe zorlamak için bir çözümleyiciye ihtiyaç duyar.

  • LDM 2026-07-22: Bir bildirimi gerekli-güvenli değil olarak işaretleyebilecek unsafe her yerde bildirim değiştirici olarak izin ver safe

(yanıtlandı) unsafe üyelerde bağlam varsayılanları

Bir yöntemin tüm gövdesini otomatik olarak bağlam unsafeunsafe haline getirmemeye karar verebilirdik. Rust bunu RFC 2585'te, blokların unsafe kapsamını aslında kullanılan konumlarla unsafe azaltmaya yardımcı olan motivasyonla yaptı. C# dilinde de benzer motivasyonlarla uyarı veya hata olarak aynı şeyi yapabiliriz.

(yanıtlandı) new() Kısıtlama

new() (şu anda derleyicide diğer özellikler için desteklemediğimiz bir şey, gibi) ile desteklemek Obsolete istiyor muz?

M<C>(); // should be an error outside `unsafe` context since `M` calls the requires-unsafe `C..ctor`?

void M<T>() where T : new()
{
    _ = new T();
}

class C
{
    unsafe public C() { }
}
  • LDM 2026-05-13: requires-unsafe parametresiz oluşturuculara sahip türler kısıtlamayı new() karşılamamalıdır

new() kısıtlama ve usings

Diğer adlarda ve statik kullanmalarda nasıl davranmalıdır?

  • Bildirimde using hata olması, zaten desteklediğimiz anahtar sözcük aracılığıyla gizlenebilir unsafe olması veya
  • kullanım sitesinde normal olarak bir hata olması gerekir mi? Bunun, doğrudan diğer ad veya statik kullanım olmadan kullanılması gibi bir hata olması gerekir mi?

Note

İkinci durumda, diğer adları ve statik kullanımları kullanmak için "anlamsız unsafe" uyarı eklememiz gerekir.

class C
{
    unsafe public C() { }
}

class D<T> where T : new()
{
    public static void M() { _ = new T(); }
}
using X = D<C>;
using unsafe X = D<C>;

X.M();
using static D<C>;
using static unsafe D<C>;

M();

Diğer kısıtlamaların bugün eski seçenek gibi davrandığını unutmayın:

using X = D<C>; // error here

_ = new X(); // ok
_ = new D<C>(); // error here

class C
{
    public C(int x) { }
}

class D<T> where T : new();

Daha fazla yapı olmalıdır unsafe?

  • dynamic (büyük olasılıkla BCL'nin yansıma API'leri için kararıyla eşleşmelidir)

(yanıtlandı) Ne kadar kırılmak istiyoruz?

Soru metni

İlk teklif, esas olarak ne kadar agresif olmak istediğimize yönelik bir litus testi olarak en yüksek düzeyde kırılmaya neden olan bir yaklaşımdır. Kodun bölümlerini kabul etme/devre dışı bırakma olanağı önermez, yöntemlerin unsafe anlamını değiştirir, türlerde kullanımını unsafe engeller, uyarılar yerine hatalar kullanır ve genellikle geçişi derleyici yükseltilirken (ve bağımlılıklar güncelleştirilip zaten kullanımda olan üyelere eklendikçe unsafe ) bir kerede yinelenmeye zorlar. Ancak, kesintilerin boyutunu kapsamak ve artımlı benimsemeye izin vermek için üzerine çizebileceğimiz bu tür değişiklikler yapma konusunda zengin bir deneyime sahibiz. Bu seçenekler aşağıda ele alınmıştır.

Kod bölgelerini kabul etme/devre dışı bırakma

C# eklenmemiş kodun "temel" büyük/küçük harf durumunu ilk kez yeniden tanımlanmamıştır. C# 8.0, çoğu şekilde özelliğin nasıl unsafe şekillendiğine yönelik bir şema olarak görülebilen null atanabilir başvuru türü özelliğini kullanıma sunar. Benzer hedeflere (varsayılan C# yorumlanma şeklini yeniden tanımlayarak milyarlarca dolara mal olan hataları önleme) ve benzer bir genel özellik kümesi (durumları yaymak ve hataları önlemek için türlere yeni bilgiler ekleyin) vardı. Ayrıca büyük ölçüde hataya neden oldu ve özelliğin kod temelleri tarafından zaman içinde benimsenebilmesi için güçlü bir kabul etme ve geri çevirme işlevselliğine ihtiyaç duyuldu. Bu işlev "null atanabilir başvuru türü bağlamı"dır. Bu, derleyiciyi koddaki belirli bir bölge için hem açıklamasız tür başvurularının nasıl yorumlandığını hem de kullanıcıya verilecek uyarı türlerini bildiren sözcük temelli bir kapsamdır. Bu yeni kuralların uygulanıp uygulanmadığını denetlemeye olanak sağlamak için bir "güvenlik kuralları bağlamı" veya benzeri eklemek için unsafe de bunu model olarak kullanabiliriz.

Yeni unsafe özelliklerle sahip olduğumuz avantajlardan biri, bunların çok daha az yaygın olmasıdır. Üst kitaplıklarda iyi sayıda unsafe çağrı olsa da, en çok kullanılan unsafe kitaplıkların yüzdesine ilişkin tahminlerimiz "C# kodunun her satırı yazılan her satırdan" çok daha düşüktür. Umalım ki bu, kabul etme/devre dışı bırakma özelliğine ihtiyaç duyulsa da, ayrılmış ön işlemci anahtarları ve benzeri ile null atanabilir bir mekanizmaya ihtiyaç duymadığımız anlamına gelir.

Uyarılar ve hatalar

Teklifte şu anda bellek güvenliği gereksinimlerinin hata yerine bir uyarı yoluyla uygulandığı belirtiliyor. Bu, uyarıların yeni özelliği artımlı olarak benimsemesine izin verilen ve büyük kod dalgalarını bir kerede dönüştürmesi gerekmeyen null atanabilir özellik ile çalışma deneyimimizden geliyor. Güvenli olmayan uyarılar için de benzer bir süreç gerekeceğini tahmin ediyoruz: Birçok kod temeli, yeni kuralları küresel olarak açıp yaşamlarına devam edebilecektir. Ancak yeni kuralları benimsemeyi en çok önemsediğimiz kod temellerinin açıklama eklemek için büyük miktarda koda sahip olmasını bekliyoruz ve hata duvarı görmek ve hemen vazgeçmek yerine bu özelliği kullanmaya devam edebilmelerini istiyoruz. Gereksinimlerle ilgili uyarılar yaparak, bu kod temellerinin uyarıları gerektiği gibi dosyaya göre veya yönteme göre düzelterek uyarıları başka her yerde devre dışı bırakmasına izin veririz.

Yöntem imzası sonları

Şu anda yöntemdeki bir anahtar sözcük olarak anlamsal etkisi olmayan ve sözcük temelli kapsamı olmayan bir öğeye sözcük temelli kapsamlı olmayan bir öğeden geçiş yapmayı öneriyoruz unsafe . Bir yöntemin veya üyenin çağıranın bağlam içinde olması gerektiğinde (örneğin, unsafe değiştirici olarak) yeni bir callerunsafe anahtar sözcük ekleyerek bu kesmeyi sınırlayabiliriz.

Kaynak oluşturucular için varsayılanlar

Null atanabilir için, oluşturucu yazarlarını projenin tamamının özelliği varsayılan olarak kabul edip etmediğinden bağımsız olarak null atanabilir seçeneğini açıkça kabul etmeye zorlarız. Böylece oluşturucu çıkışı, kullanıcı null atanabilir duruma gelip hata olarak uyararak bozulmaz. Kaynak oluşturucular için de aynısını yapmalı mıydık?

Sonuç

LDM 2025-11-05'te yanıtlandı. Yeni kurallar açıldığında bellek güvenliği sorunlarıyla ilgili hatalar bildireceğiz ve kaynak oluşturucular için özel durum oluşturulmayacak.

(yanıtlandı) Hatalar mı yoksa uyarılar mı?

(yanıtlandı) Kaynak oluşturucu affordance

(yanıtlandı) Blokları veya işaretçileri olan safe üyeler için oluşturucu gerekli kılınsın unsafe mı?

(yanıtlandı) Kabul edilen olmayan arayanlar için de uyumluluk modu mu?

Yanıt: İşaretçileri imzada olan kabul edilmeyen üyeler, kabul edilen arayanlar için güvenli değil olarak kabul edilir.

(yanıtlandı) Uyumluluk modu uzatılasın mı?

ve'i nint de işaretçi olarak düşünsek System.IntPtr mi? İstenmeyen arayanlardan da extern olarak mı düşünmeliyiz/DllImport? Kabul edilen bir derleme, kabul edilmeyen bir derlemeye başvurduğunda bir paket uyarımız mı olmalıdır?

  • LDM 2026-04-29: uzantı yok (çözümleyiciler bazı daha az belirli güvenli olmayan sinyalleri kapsayabiliyor)