2012-03-29 25 views
9

Właściwie powinienem był zapytać: jak mogę to zrobić: i pozostają zgodne z CLS? Ponieważ jedyny sposób, w jaki mogę to zrobić, jest następujący, ale użycie albo __makeref, FieldInfo.SetValueDirect lub po prostu System.TypedReference generalnie unieważnia zgodność CLS.Czy mogę ustawić wartość na struct przez odbicie bez boksowania?

// code illustrating the issue: 
TestFields fields = new TestFields { MaxValue = 1234 }; // test struct with one field 

FieldInfo info = fields.GetType().GetField("MaxValue"); // get the FieldInfo 

// actual magic, no boxing, not CLS compliant: 
TypedReference reference = __makeref(fields); 
info.SetValueDirect(reference, 4096); 

Odpowiednikiem zgodny z SetValueDirect jest SetValue, ale zajmuje obiekt jako cel, stąd moja struct zostaną zapakowane, co mnie ustawienie wartości na kopii, a nie zmiennej pierwotnej.

Ogólny odpowiednik dla SetValue nie istnieje, o ile wiem. Czy istnieje inny sposób ustawiania pola (odniesienie do) struktury poprzez odbicie?

Odpowiedz

5

Bądź opakowanie zgodne z CLS na SetValueDirect:

var item = new MyStruct { X = 10 }; 

    item.GetType().GetField("X").SetValueForValueType(ref item, 4); 


[CLSCompliant(true)] 
static class Hlp 
{ 
    public static void SetValueForValueType<T>(this FieldInfo field, ref T item, object value) where T : struct 
    { 
    field.SetValueDirect(__makeref(item), value); 
    } 
} 
+0

Może nie rozumiem CLSCompliancy, myślałem, że oznacza to, że nie można używać \ "niezgodnych funkcji. Jeśli jest to dozwolone, to naprawdę ułatwia to zadanie. – Abel

+1

@Abel: Zgodność z CLS oznacza, że ​​Twoi członkowie publiczni odwołują się do typów zgodnych z CLS. Nie mówi nic o tym, co zawiera się w twoich członkach. – Gabe

+0

Ups, moja edycja komentarza została utracona. Tak, zauważyłem, wynika to z [trzech pierwszych naboi na MSDN na zgodność z CLS] (http://msdn.microsoft.com/en-us/library/bhc3fa7f.aspx). Nadal jest strasznie dziwne, że potrzebuję [nieudokumentowanego słowa kluczowego __makeref] (http://www.codeproject.com/Articles/38695/UnCommon-C-keywords-A-Look#makref), aby to zadziałało. – Abel

2

Nie jestem pewien, czy to będzie pasować do twoich ograniczeń, ale deklarując wystąpienie struct jako ValueType, SetValue będzie działać zgodnie z oczekiwaniami.

ValueType fields = new TestFields { MaxValue = 1234 }; // test struct with one field 
    FieldInfo info = typeof(TestFields).GetField("MaxValue"); // get the FieldInfo 
    info.SetValue(fields, 4096); 
    Console.WriteLine(((TestFields)fields).MaxValue); // 4096 

Aby uzyskać więcej informacji, zobacz numer this answer.

+1

+1: Twoja odpowiedź jest z pewnością interesująca, ale kod _nie_ działają na oryginalnej struktury. Zamiast tego jest zapakowany do SetValue i unboxed, ponieważ rzutuje go do użycia z WriteLine. Aby to zobaczyć, sprawdź IL. – Abel

+0

Nie patrzyłem na IL, ale sprawdziłem, że ostatnia linia rzeczywiście wyświetla 4096, więc w jaki sposób może nie działać na oryginalnym egzemplarzu? –

+0

OK, teraz spojrzałem na IL. Nie mówię zbyt płynnie, ale widzę, o co w tej skrzynce mówisz. Z sekcji locals deklaruje dwa wystąpienia TestFields, gdy C# ma tylko jeden. Bardzo dziwny. Czy to rzeczywiste boksowanie, czy też wartość jest ustawiona poprawnie? –

6

pod względem właściwości, jeśli masz typy struct i własności, można utworzyć delegata z seter nieruchomości. Jak podkreślić, pola nie mają ustawiające, ale można założyć, że zachowuje się dokładnie tak samo:

delegate void RefAction<T1, T2>(ref T1 arg1, T2 arg2); 

struct TestFields 
{ 
    public int MaxValue; 

    public int MaxValueProperty 
    { 
     get { return MaxValue; } 
     set { MaxValue = value; } 
    } 
}; 

static class Program 
{ 
    static void Main(string[] args) 
    { 
     var propertyInfo = typeof(TestFields).GetProperty("MaxValueProperty"); 
     var propertySetter = (RefAction<TestFields, int>)Delegate.CreateDelegate(typeof(RefAction<TestFields, int>), propertyInfo.GetSetMethod()); 

     var fieldInfo = typeof(TestFields).GetField("MaxValue"); 

     var dynamicMethod = new DynamicMethod(String.Empty, typeof(void), new Type[] { fieldInfo.ReflectedType.MakeByRefType(), fieldInfo.FieldType }, true); 
     var ilGenerator = dynamicMethod.GetILGenerator(); 
     ilGenerator.Emit(OpCodes.Ldarg_0); 
     ilGenerator.Emit(OpCodes.Ldarg_1); 
     ilGenerator.Emit(OpCodes.Stfld, fieldInfo); 
     ilGenerator.Emit(OpCodes.Ret); 
     var fieldSetter = (RefAction<TestFields, int>)dynamicMethod.CreateDelegate(typeof(RefAction<TestFields, int>)); 

     var fields = new TestFields { MaxValue = 1234 }; 
     propertySetter(ref fields, 5678); 
     fieldSetter(ref fields, 90); 
     Console.WriteLine(fields.MaxValue); 
    } 
} 
+0

Tak, to prawda, ale moje pytanie dotyczy pól, a nie właściwości. Niewielka, ale ważna różnica: pola nie mają dedykowanego ustawiacza. – Abel

+0

Ups, prawda, przepraszam za to, edytuję. – hvd

+0

@Abel Nagle wydaje się mniej elegancki, ale nadal działa z polami. (Możesz buforować utworzone metody, jeśli często ich używasz.) – hvd

Powiązane problemy