Cannot get PropertyInfo SetValue to Work for a Structure

C

Charles Law

I have adapted the following code from the MSDN help for PropertyInfo
SetValue. In the original code, the structure MyStructure is defined as a
class MyProperty, and it works as expected. There is also a minor change in
class Mypropertyinfo, which I have commented out.

When using a structure, however, the second call to GetValue returns
"Default caption". Can anyone tell me why, and how I can make this work?

<code>
Imports System
Imports System.Reflection

Public Structure MyStructure

Private myCaption As String

Public Property Caption() As String
Get
Return myCaption
End Get
Set(ByVal Value As String)
If myCaption <> Value Then
myCaption = Value
End If
End Set
End Property
End Structure

Class Mypropertyinfo

Public Shared Function Main() As Integer

Console.WriteLine("Reflection.PropertyInfo")

''Dim af As New MyProperty ' original
Dim af As MyStructure

af.Caption = "Default caption"

' Get the type and PropertyInfo
Dim MyType As Type = af.GetType
Dim Mypropertyinfo As PropertyInfo = MyType.GetProperty("Caption")

' Get and display the GetValue method
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(af,
Nothing).ToString())

' Use the SetValue method to change the caption
Mypropertyinfo.SetValue(af, "This caption has been changed.",
Nothing)

' Get the caption again and display it
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(af,
Nothing).ToString())

Console.ReadLine()

Return 0

End Function
End Class
</code>

TIA

Charles
 
I

Imran Koradia

This is because a structure is a value type and both SetValue and GetValue
expect Objects causing the arguments to boxed. As a result, the GetValue and
SetValue are working the boxed object rather than on the actual structure. I
believe this technique won't work on structures - I could be wrong though.

hope that helps..
Imran.
 
C

Charles Law

Hi Imran

Thanks for a quick response. It does make sense, but sadly, if it is true, I
am a bit stuck, as I need to be able to use this or a similar technique to
set property values.

Do you, or anyone else know of a workaround, or another way of doing it?

Charles
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
The "easiest" way would be not to use a Structure.

Otherwise I believe you will need to use C#, as VB.NET adds calls to a
RuntimeHelpers.GetObjectValue whenever you try to do something with an
object variable.

The "problem" with using a Structure is that a Structure is a value type,
hence the call to GetObjectValue which "boxes a value type". GetObjectValue
is used to ensure that when you assign a value type in an object variable to
another object variable a copy of the value is made, not a reference to the
same instance. Which is in keeping with how typed value types work.

You can use ILDASM.EXE to see when VB.NET adds calls to
RuntimeHelpers.GetObjectValue.

I would suggest you try CallByName, however it appears to call
RuntimeHelpers.GetObjectValue first also...

Hope this helps
Jay
 
C

Charles Law

Hi Jay

I take your point about not using a structure. Unfortunately, the structure
is in a library already in use by the client, so I don't really have the
option of changing it to a class. I have just tried CallByName, but as you
suggest it exhibits the same problem.

Can you think of any way to circumvent the boxing? I take it that it is the
first parameter to SetValue that is being boxed. Would there be a way of
boxing the original structure so that all operations occur on the same
object?

Charles
 
I

Imran Koradia

Jay B. Harlow said:
Charles,
The "easiest" way would be not to use a Structure.

Otherwise I believe you will need to use C#, as VB.NET adds calls to a
RuntimeHelpers.GetObjectValue whenever you try to do something with an
object variable.

The "problem" with using a Structure is that a Structure is a value type,
hence the call to GetObjectValue which "boxes a value type". GetObjectValue
is used to ensure that when you assign a value type in an object variable to
another object variable a copy of the value is made, not a reference to the
same instance. Which is in keeping with how typed value types work.

You can use ILDASM.EXE to see when VB.NET adds calls to
RuntimeHelpers.GetObjectValue.

I would suggest you try CallByName, however it appears to call
RuntimeHelpers.GetObjectValue first also...

Jay,

That's true. Even CallByName does not work for structures. I would be
interested to know if there was indeed a way to Get/Set property values for
structures via Reflection or any other technique (apart from the straight
forward way).

Imran.
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
The only way to circumvent the boxing is to use a Reference type.

I believe the only way to circumvent the GetObjectValue is to use C# (I have
not looked at the IL produced by C# enough to say for certain, not at a
point where I can check right now).

As I tried to state: if you have an Object variable VB.NET will call
GetObjectValue when ever it needs the "value" of that variable. Which means
GetObjectValue gets called on assignments & for parameters.

First I would change the try to change the structure to a Class. If that was
not possible, I would define a small C# function that accepted the structure
as a parameter, modified the fields, then returned the modified structure
back to VB. You could use your existing Reflection code to do this. (a Value
Type CallByName replacement if you will).

Hope this helps
Jay
 
C

Charles Law

Jay

I don't use C# - would it be too much to ask for a skeleton that I could
use?

Charles
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
You can basically use the VB.NET code you started with:

Something like:

In a C# class library:

' don't want other inheriting from this class
public sealed class PropertyHelper
{

' Don't want others instantiating this class
private PropertyHelper()
{
}

public static object SetValue(object o, string name, object value)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, null);
return o;
}

public static object SetValue(object o, string name, object value,
object[] index)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, index);
return o;
}

}

Then in VB.NET when you want to use it you can:

Imports ClassLibrary1 ' whatever you called the above class library.
Dim af As MyStructure

af = DirectCast(PropertyHelper.SetValue(af, "Caption", "This caption has
been changed."), MyStructure)

Hope this helps
Jay



Charles Law said:
Jay

I don't use C# - would it be too much to ask for a skeleton that I could
use?

Charles
<<snip>>
 
C

Charles Law

Jay, I love you, and want to have your babies. It does exactly what I want.

Thanks.

Charles


Jay B. Harlow said:
Charles,
You can basically use the VB.NET code you started with:

Something like:

In a C# class library:

' don't want other inheriting from this class
public sealed class PropertyHelper
{

' Don't want others instantiating this class
private PropertyHelper()
{
}

public static object SetValue(object o, string name, object value)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, null);
return o;
}

public static object SetValue(object o, string name, object value,
object[] index)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, index);
return o;
}

}

Then in VB.NET when you want to use it you can:

Imports ClassLibrary1 ' whatever you called the above class library.
Dim af As MyStructure

af = DirectCast(PropertyHelper.SetValue(af, "Caption", "This caption
has been changed."), MyStructure)

Hope this helps
Jay



Charles Law said:
Jay

I don't use C# - would it be too much to ask for a skeleton that I could
use?

Charles
<<snip>>
 
J

Jay B. Harlow [MVP - Outlook]

Charles,
This just in!

If you assign your structure to a System.ValueType variable instead of a
System.Object variable you can call the methods via reflection.

For example (from your original post):

Public Shared Function Main() As Integer

Console.WriteLine("Reflection.PropertyInfo")

''Dim af As New MyProperty ' original
Dim af As MyStructure

af.Caption = "Default caption"

Dim vaf As ValueType = af

' Get the type and PropertyInfo
Dim MyType As Type = vaf.GetType
Dim Mypropertyinfo As PropertyInfo = MyType.GetProperty("Caption")

' Get and display the GetValue method
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(vaf,
Nothing).ToString())

' Use the SetValue method to change the caption
Mypropertyinfo.SetValue(vaf, "This caption has been changed.",
Nothing)

' Get the caption again and display it
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(vaf,
Nothing).ToString())

Console.ReadLine()

Return 0

End Function

Notice that I define a vaf variable that has type System.ValueType, that
this new variable is used when calling Reflection.

This tip courtesy of Mattias Sjögren in the thread "Difference between
boxing a primitive type and boxing a custom structure" in the
microsoft.public.dotnet.framework newsgroup.

Hope this helps
Jay


Charles Law said:
Jay, I love you, and want to have your babies. It does exactly what I
want.

Thanks.

Charles


Jay B. Harlow said:
Charles,
You can basically use the VB.NET code you started with:

Something like:

In a C# class library:

' don't want other inheriting from this class
public sealed class PropertyHelper
{

' Don't want others instantiating this class
private PropertyHelper()
{
}

public static object SetValue(object o, string name, object value)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, null);
return o;
}

public static object SetValue(object o, string name, object value,
object[] index)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, index);
return o;
}

}

Then in VB.NET when you want to use it you can:

Imports ClassLibrary1 ' whatever you called the above class library.
Dim af As MyStructure

af = DirectCast(PropertyHelper.SetValue(af, "Caption", "This caption
has been changed."), MyStructure)

Hope this helps
Jay



Charles Law said:
Jay

I don't use C# - would it be too much to ask for a skeleton that I could
use?

Charles
<<snip>>
 
C

Charles Law

Top tip! Thanks Jay (and Mattias)

And casting vaf back to af after calling SetValue gives me the updated
structure in af as a MyStructure again.

Cheers.

Charles


Jay B. Harlow said:
Charles,
This just in!

If you assign your structure to a System.ValueType variable instead of a
System.Object variable you can call the methods via reflection.

For example (from your original post):

Public Shared Function Main() As Integer

Console.WriteLine("Reflection.PropertyInfo")

''Dim af As New MyProperty ' original
Dim af As MyStructure

af.Caption = "Default caption"

Dim vaf As ValueType = af

' Get the type and PropertyInfo
Dim MyType As Type = vaf.GetType
Dim Mypropertyinfo As PropertyInfo = MyType.GetProperty("Caption")

' Get and display the GetValue method
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(vaf,
Nothing).ToString())

' Use the SetValue method to change the caption
Mypropertyinfo.SetValue(vaf, "This caption has been changed.",
Nothing)

' Get the caption again and display it
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(vaf,
Nothing).ToString())

Console.ReadLine()

Return 0

End Function

Notice that I define a vaf variable that has type System.ValueType, that
this new variable is used when calling Reflection.

This tip courtesy of Mattias Sjögren in the thread "Difference between
boxing a primitive type and boxing a custom structure" in the
microsoft.public.dotnet.framework newsgroup.

Hope this helps
Jay


Charles Law said:
Jay, I love you, and want to have your babies. It does exactly what I
want.

Thanks.

Charles


Jay B. Harlow said:
Charles,
You can basically use the VB.NET code you started with:

Something like:

In a C# class library:

' don't want other inheriting from this class
public sealed class PropertyHelper
{

' Don't want others instantiating this class
private PropertyHelper()
{
}

public static object SetValue(object o, string name, object
value)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, null);
return o;
}

public static object SetValue(object o, string name, object
value, object[] index)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, index);
return o;
}

}

Then in VB.NET when you want to use it you can:

Imports ClassLibrary1 ' whatever you called the above class library.
Dim af As MyStructure

af = DirectCast(PropertyHelper.SetValue(af, "Caption", "This caption
has been changed."), MyStructure)

Hope this helps
Jay



Jay

I don't use C# - would it be too much to ask for a skeleton that I
could use?

Charles


<<snip>>
 
I

Imran Koradia

Wow - nice one, Jay. I was a bit puzzled why this wouldn't work. Of course,
after looking at the solution it seems pretty obvious :) Thanks for this tip
(and thanks to Mattias Sjögren as well).

Imran.

Jay B. Harlow said:
Charles,
This just in!

If you assign your structure to a System.ValueType variable instead of a
System.Object variable you can call the methods via reflection.

For example (from your original post):

Public Shared Function Main() As Integer

Console.WriteLine("Reflection.PropertyInfo")

''Dim af As New MyProperty ' original
Dim af As MyStructure

af.Caption = "Default caption"

Dim vaf As ValueType = af

' Get the type and PropertyInfo
Dim MyType As Type = vaf.GetType
Dim Mypropertyinfo As PropertyInfo = MyType.GetProperty("Caption")

' Get and display the GetValue method
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(vaf,
Nothing).ToString())

' Use the SetValue method to change the caption
Mypropertyinfo.SetValue(vaf, "This caption has been changed.",
Nothing)

' Get the caption again and display it
Console.WriteLine("GetValue - " & Mypropertyinfo.GetValue(vaf,
Nothing).ToString())

Console.ReadLine()

Return 0

End Function

Notice that I define a vaf variable that has type System.ValueType, that
this new variable is used when calling Reflection.

This tip courtesy of Mattias Sjögren in the thread "Difference between
boxing a primitive type and boxing a custom structure" in the
microsoft.public.dotnet.framework newsgroup.

Hope this helps
Jay


Charles Law said:
Jay, I love you, and want to have your babies. It does exactly what I
want.

Thanks.

Charles


Jay B. Harlow said:
Charles,
You can basically use the VB.NET code you started with:

Something like:

In a C# class library:

' don't want other inheriting from this class
public sealed class PropertyHelper
{

' Don't want others instantiating this class
private PropertyHelper()
{
}

public static object SetValue(object o, string name, object
value)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, null);
return o;
}

public static object SetValue(object o, string name, object
value, object[] index)
{
System.Reflection.PropertyInfo info =
o.GetType().GetProperty(name);
info.SetValue(o, value, index);
return o;
}

}

Then in VB.NET when you want to use it you can:

Imports ClassLibrary1 ' whatever you called the above class library.
Dim af As MyStructure

af = DirectCast(PropertyHelper.SetValue(af, "Caption", "This caption
has been changed."), MyStructure)

Hope this helps
Jay



Jay

I don't use C# - would it be too much to ask for a skeleton that I
could use?

Charles


<<snip>>
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top