Yansıma kullanarak C # 'da özel bir salt okunur alanı değiştirebilir miyim?


115

Düşünüyorum da, yansıma kullanılarak pek çok şey yapılabildiğinden, kurucu yürütmeyi tamamladıktan sonra özel bir salt okunur alanı değiştirebilir miyim?
(not: sadece merak)

public class Foo
{
 private readonly int bar;

 public Foo(int num)
 {
  bar = num;
 }

 public int GetBar()
 {
  return bar;
 }
}

Foo foo = new Foo(123);
Console.WriteLine(foo.GetBar()); // display 123
// reflection code here...
Console.WriteLine(foo.GetBar()); // display 456

Yanıtlar:


151

Yapabilirsin:

typeof(Foo)
   .GetField("bar",BindingFlags.Instance|BindingFlags.NonPublic)
   .SetValue(foo,567);

2
Elbette kesinlikle haklısın. Özür dilerim. Ve evet, denedim, ancak bir destek alanı kullanmadan doğrudan salt okunur özelliği ayarlamaya çalıştım. Denediğim şey hiçbir anlam ifade etmiyordu. Çözümün mükemmel cezası (tekrar test, doğru bu kez) işleri
Adaçayı Pourpre

bunu moq ile nasıl yapabiliriz?
l --''''''--------- '' '' '' '' '' ''

Dotnet core 3.0'da bu artık mümkün değil. Bir System.FieldAccessException şöyle bildirilir: "'Foo' türü başlatıldıktan sonra ilk olarak statik alan 'bar' ayarlanamaz."
David Perfors

54

Açık olan şey, onu denemektir:

using System;
using System.Reflection;

public class Test
{
    private readonly string foo = "Foo";

    public static void Main()
    {
        Test test = new Test();
        FieldInfo field = typeof(Test).GetField
            ("foo", BindingFlags.Instance | BindingFlags.NonPublic);
        field.SetValue(test, "Hello");
        Console.WriteLine(test.foo);
    }        
}

Bu iyi çalışıyor. (Java'nın farklı kuralları vardır, ilginçtir - Fielderişilebilir olması için açıkça ayarlamanız gerekir ve zaten yalnızca örnek alanlar için çalışacaktır.)


4
Ahmed - ama bunu yapmak için dili kullanmıyoruz, bu yüzden dil şartnamesi oy alamaz ...
Marc Gravell

4
Evet - dilin istediğini "bozan" yapılabilecek çok şey var. Örneğin, tür başlatıcıları birden çok kez çalıştırabilirsiniz.
Jon Skeet

28
Ayrıca, bugün bazı uygulamalarda yapabilmeniz, her uygulama için her zaman yapabileceğiniz anlamına gelmediğini de not ediyorum. Salt okunur alanların yansıma yoluyla değiştirilebilir olması gerektiğini belgelediğimiz herhangi bir yerin farkında değilim. Bildiğim kadarıyla, CLI'nin uyumlu bir uygulaması, kurucu tamamlandıktan sonra yansıma yoluyla değiştirildiğinde istisnalar atacak şekilde salt okunur alanları uygulamak için tamamen özgürdür.
Eric Lippert

3
Yine de bu iyi değil, çünkü bir sınıfı orijinal tasarlandığından daha fazla genişletmem gereken durumlar var. İyi planlandığında kapsüllemeyi geçersiz kılmanın her zaman bir yolu olmalıdır. Tek alternatif kanalıdır ve bazen çerçevenin bir bölümünü yeniden yazmadan mümkün değildir.
drifter

5
@drifter: O noktada, kendinizi acı dolu bir dünyaya açıyorsunuz. Gelecekteki bir sürümde kolayca değiştirilebilecek mevcut uygulama ayrıntılarına güveniyorsunuz.
Jon Skeet

11

Genel olarak ve özellikle E. Lippert'in bunun belgelenmiş bir davranış olmadığı ve dolayısıyla geleceğe yönelik bir kod olmadığı yönündeki yorumuyla , diğer cevaplara katılıyorum .

Ancak bir başka sorunu da fark ettik. Kodunuzu kısıtlı izinlere sahip bir ortamda çalıştırıyorsanız, bir istisna yaşayabilirsiniz.

Kodumuzun makinelerimizde düzgün çalıştığı bir durum yaşadık, ancak VerificationExceptionkod kısıtlı bir ortamda çalıştığında bir aldık . Suçlu, salt okunur bir alanın belirleyicisine bir yansıma çağrısıydı. Bu alanın salt okunur kısıtlamasını kaldırdığımızda işe yaradı.


2
Hangi ortamların Doğrulama
İstisnası

4

Kapsüllemeyi neden böyle kırmak istediğini sordun.

Varlıkları hidratlamak için bir varlık yardımcı sınıfı kullanıyorum. Bu, yeni bir boş varlığın tüm özelliklerini almak için yansıma kullanır ve özellik / alan adını sonuç kümesindeki sütunla eşleştirir ve propertyinfo.setvalue () kullanarak bunu ayarlar.

Başka kimsenin değeri değiştirmesini istemiyorum, ancak tüm çabayı her varlık için özel kod hidrasyon yöntemlerine harcamak da istemiyorum.

Depolanan procs'larımın çoğu, tablolara veya görünümlere doğrudan karşılık gelmeyen sonuç kümeleri döndürüyor, bu nedenle ORM kod üretimi benim için hiçbir şey yapmıyor.


1
Ayrıca, değerin kodlanmış olduğu veya sağlayamayacağım bir yapılandırma dosyası gerektirdiği bazı API sınırlamalarını aşmak için de kullandım. (Örneğin, derlemeler yansıtma yoluyla yüklendiğinde DIME ekleri için WSE 2.0 dosya boyutu)
StingyJack

3

Güvenli olmayan kullanarak bunu yapmanın başka bir basit yolu (veya alanı DLLImport aracılığıyla bir C yöntemine aktarabilir ve orada ayarlayabilirsiniz).

using System;

namespace TestReadOnly
{
    class Program
    {
        private readonly int i;

        public Program()
        {
            i = 66;
        }

        private unsafe void ForceSet()
        {
            fixed (int* ptr = &i) *ptr = 123;
        }

        static void Main(string[] args)
        {
            var program = new Program();
            Console.WriteLine("Contructed Value: " + program.i);
            program.ForceSet();
            Console.WriteLine("Forced Value: " + program.i);
        }
    }
}

2

Cevap evet, ama daha da önemlisi:

Neden istiyorsun Kasıtlı olarak kapsüllemeyi kırmak bana korkunç derecede kötü bir fikir gibi görünüyor.

Bir salt okunur veya sabit alanı değiştirmek için yansımayı kullanmak , İstenmeyen Sonuçlar Yasasını Murphy Yasası ile birleştirmek gibidir .


1
cevap, yukarıda belirtildiği gibi "sadece meraktır".
Ron Klein

Yapabileceğim en iyi kodu yazmak için kendimi sadece bu numarayı yapmak zorunda bulduğum zamanlar oluyor. Örnek olay
sparker

3
Ben de bu numarayı, herhangi bir iş kodunda değiştirilmemesi gereken varsayılan değerleri geçersiz kılmak için birim test projelerinde kullanıyorum ...
Koen

Ve test amacıyla temel sınıf kitaplıklarında özel dahili özellikleri, özellikle MS'nin her şeyi özel, dahili ve özelliklerde ayarlayıcı olmadan işaretlediği Üyelik API'sini ayarlamaya çalışıyordum. Bunun için durumlar var, ancak soru sizin kontrolünüzdeki bir API'ye uygulanıyorsa
Chad Grant

3
Mantıklı olduğu durumlar var. NHibernate gibi O / R haritacıları bunu hidrasyon için her zaman yapar, çünkü bu, kalıcı varlıklar için veri kapsüllemeyi ilk etapta uygulayabilmenin tek yoludur.
chris

2

Bunu yapma.

Nesnelerin kendi beyan edilmiş türleri olamayacağı gerçeküstü bir hatayı düzeltmek için bir gün geçirdim.

Salt okunur alanını değiştirmek bir kez çalıştı. Ancak tekrar değiştirmeyi denerseniz, aşağıdaki gibi durumlar yaşarsınız:

SoundDef mySound = Reflection_Modified_Readonly_SoundDef_Field;
if( !(mySound is SoundDef) )
    Log("Welcome to impossible-land!"); //This would run

Öyleyse yapma.

Bu, Mono çalışma zamanında (Unity oyun motoru) idi.


2
Bilginize - Unity Engine, bunun gibi derin C # diline özgü soruları etkili bir şekilde yanıtlamak için kullanılamaz çünkü Unity, bir anlamda .cs'ler komut dosyasıymış gibi kendi C # derlemesini gerçekleştirir. Fikrinizin geçerli olmadığını söylemiyorum, ancak kesinlikle Unity Engine ve C # birlikte özeldir.
Dave Jellison

0

Bunları birim testi için yapmanız gerekiyorsa, şunları kullanabileceğinizi eklemek istiyorum:

A) PrivateObject sınıfı

B) Yine de bir PrivateObject örneğine ihtiyacınız olacak, ancak Visual Studio ile "Erişimci" nesneleri oluşturabilirsiniz. Nasıl yapılır: Özel Erişicileri Yeniden Oluşturma

Kodunuzdaki bir nesnenin özel alanlarını birim testinin dışında ayarlıyorsanız, bu bir "kod kokusu" örneğidir.Bence bunu yapmak istemenizin tek diğer nedeni, üçüncü bir tarafla ilgileniyorsanız olabilir. kitaplık ve hedef sınıf kodunu değiştiremezsiniz. O zaman bile, muhtemelen 3. tarafla iletişim kurmak, durumunuzu açıklamak ve devam edip etmeyeceklerini görmek ve ihtiyacınızı karşılamak için kodlarını değiştirmek istersiniz.

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.