ValueType object

C

colin

Hi,
Im a bit confused about when you cast a value type to an object.
I have a property grid wich stores the data and the defualt as
objects.

the data is limited to primitive types, structs and strings.
the structs only have primitive types.

however once ive set the data to the defualt,
if the data is a struct then any edits change the defualt data as well
wich was unexpected !

however it doesnt happen for primitive types.

I thought value types were always passed by value,
does this mean an object is actually able to behave
as a pointer to a struct ?

I gues il have to identify if its a struct and copy it with reflection ?...
but why is there not even Type.IsStruct ?
would this be the same as !IsClass&&!IsInterface&&!IsPrimitive ?

Colin =^.^=
 
M

Marc Gravell

I have a property grid wich stores the data and the defualt as

Well, you haven't really explained this setup... and unfortunately the
devil is probably in the detail here... can you add more info?
however once ive set the data to the defualt,
if the data is a struct then any edits change the defualt data as well
wich was unexpected !

I'm guessing that your structs are "mutable" - i.e. you can edit them
once created. This is almost always a big mistake; where "almost" means
"it would take a long discussion to explain some borderline scenarios
where it might just possibly make sense to have a mutable struct". In
general, structs should be /immutable/ - i.e. once created they never
change, but can be replaced (hence the need to provide CreateInstance
etc on a TypeConverter).
I thought value types were always passed by value,
does this mean an object is actually able to behave
as a pointer to a struct ?

Yes; the term is "box"; when you box a value-type to an object, an
object is created on the managed heap that encapsulates the value-type -
*broadly* as though you had a class with a single field member (although
it isn't really that).

If you then give two different bits of code the same boxed copy, then
they will be stomping all over each others values
I gues il have to identify if its a struct and copy it with reflection ?...
You can get the TypeConverter to do this for you; I'll knock up a demo...
would this be the same as !IsClass&&!IsInterface&&!IsPrimitive ?
Well, I'd go for Type.IsValueType

Marc
 
M

Marc Gravell

Et voila; an "editable" immutable struct via PropertyGrid

(quoted because it isn't actually an edit; it is a create)

Marc

using System.ComponentModel;
using System.Windows.Forms;

[TypeConverter(typeof(SomeStructConverter))]
public struct SomeStruct
{
private readonly int foo;
private readonly string bar;

public int Foo { get {return foo;}}
public string Bar { get {return bar;}}

public SomeStruct(int foo, string bar)
{
this.foo = foo;
this.bar = bar;
}
public override string ToString()
{
return string.Format("{0}:{1}", Foo, Bar);
}
}

sealed class SomeStructConverter : ExpandableObjectConverter
{
public override bool
GetCreateInstanceSupported(ITypeDescriptorContext context)
{
return true;
}
public override object CreateInstance(ITypeDescriptorContext
context, System.Collections.IDictionary propertyValues)
{
return new SomeStruct((int)propertyValues["Foo"],
(string)propertyValues["Bar"]);
}

// if you want to be able to edit the text directly
// rather than via the sub-properties, then you need
// to implement [Can]ConvertTo / [Can]ConvertFrom looking for
// string; note that ConvertTo will use ToString by
// default, so you just need [Can]ConvertFrom
}

class SomeType
{
private SomeStruct someValue;
public SomeStruct SomeValue
{
get { return someValue; }
set { someValue = value; }
}
}

static class Program
{
static void Main()
{
Application.EnableVisualStyles();
using (Form form = new Form())
using (PropertyGrid grid = new PropertyGrid())
{
grid.Dock = DockStyle.Fill;
form.Controls.Add(grid);
grid.SelectedObject = new SomeType();
Application.Run(form);
}
}
}
 
M

Marc Gravell

Here you go; a mock up, just showing a possible setup... actually I
switched to TypeDescriptor for the properties here for brevity, but in a
real system I'd probably use ICustomTypeDescriptor /
TypeDescriptionProvider...

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Windows.Forms;

[TypeConverter(typeof(FlexiObjectConverter))]
class FlexiObject
{
private readonly Dictionary<string, object> values
= new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);

internal IEnumerable<KeyValuePair<string, object>> Values
{
get { return values; }
}

public object this[string key]
{
get
{
object value;
values.TryGetValue(key, out value);
return value;
}
set
{
if (value == null)
{
values.Remove(key);
}
else
{
values[key] = value;
}
}
}
}
class FlexiObjectConverter : TypeConverter
{
public override bool GetPropertiesSupported(ITypeDescriptorContext
context)
{
return true;
}
public override PropertyDescriptorCollection
GetProperties(ITypeDescriptorContext context, object value, Attribute[]
attributes)
{
FlexiObject obj = (FlexiObject)value;
List<PropertyDescriptor> props = new List<PropertyDescriptor>();
foreach (KeyValuePair<string, object> pair in obj.Values)
{
if (pair.Value != null)
{
props.Add(new
FlexiPropertyDescriptor(pair.Value.GetType(), pair.Key));
}
}
return new PropertyDescriptorCollection(props.ToArray(), true);
}
}
class FlexiPropertyDescriptor : PropertyDescriptor
{
private readonly Type propertyType;
public FlexiPropertyDescriptor(Type propertyType, string name)
: base(name, new Attribute[0])
{
this.propertyType = propertyType;
}
public override bool IsReadOnly
{
get { return false; }
}
private FlexiObject GetFlexi(object component)
{
return (FlexiObject)component;
}
public override object GetValue(object component)
{
return GetFlexi(component)[Name];
}
public override void SetValue(object component, object value)
{
GetFlexi(component)[Name] = value;
}
public override Type ComponentType
{
get { return typeof(FlexiObject); }
}
public override bool CanResetValue(object component)
{
return true;
}
public override void ResetValue(object component)
{
GetFlexi(component)[Name] = GetDefaultValue();
}
public override Type PropertyType
{
get { return propertyType; }
}
public override bool ShouldSerializeValue(object component)
{
return !object.Equals(GetValue(component), GetDefaultValue());
}
private object GetDefaultValue() {
Type type = PropertyType;
if (type == typeof(string)) return "";
if (type.IsValueType) return Activator.CreateInstance(type);
return null;
}
}
static class Program
{
[STAThread]
static void Main()
{
FlexiObject obj = new FlexiObject();
obj["Name"] = "Marc";
obj["DOB"] = DateTime.Today;
obj["ShoeSize"] = 9;
obj["Icon"] = System.Drawing.SystemIcons.Shield;
obj["FavColour"] = System.Drawing.Color.Red;
obj["UriKind"] = System.UriKind.Relative;

Application.EnableVisualStyles();
using (Form form = new Form())
using (PropertyGrid grid = new PropertyGrid())
{
grid.Dock = DockStyle.Fill;
form.Controls.Add(grid);
grid.SelectedObject = obj;
Application.Run(form);
}
}
}
 
C

colin

many thanks again :)

I hadnt run into boxing before,
I think I understand it now thanks,
but primitive types dont seem to be affected.

all the structs are just structs,
and I have little control over them.

the data in the structs does need to be edited though thats the whole point
!

the structs could be anywhere such as in an array, or in a class or another
struct.

the defualts however arnt intended to be edited,
well at least not unintentionaly !

ive gone for Type.IsValueType &&!Type.IsPrimitive.

I cant beleive the amount of code thats gone into using this property grid
so far,
I realy wish id made my self a custom cell based control.

rather than create even more code with a type converter,
ive gone for the quick if dirty marshal way.

int size=Marshal.SizeOf(uDefaults.data);
IntPtr ptr = Marshal.AllocHGlobal(size);
Marshal.StructureToPtr(uDefaults.data, ptr, true);
udata.data=Marshal.PtrToStructure(ptr, type);
Marshal.FreeHGlobal(ptr);

seems to work even if im a little uneasy about such code.
although it will only work where the struct is initially a null object,
this suits my needs. however editing fields of sub structs doesnt
work with the boxing method, as it seems to create another copy.
seems il have to use a typedreference, but that can only be used for
the struct fields, its a shame it cant be used for the primitive fields too.

Colin =^.^=
 
M

Marc Gravell

There is no special reason that primatives aren't affected, except
that they are immutable. That is the key point here:
the data in the structs does need to be edited though

There is a 99.8% change this is a mistake [according to the department
of invented statistics].

structs in C# (or rather: ValueType in .NET) are not the same as
structs in C++; they are not a "light weight object" or however else
people think of them... the difference is the copy vs reference
semantics. I don't know enough to recommend which... but I firmly
believe that you'll have a much easier time of things if you either a:
switch to classes instead of structs, or b: make the structs
immutable.

Marc
 
R

Rene

Thanks Mark,



Nice example for the reason of the "CreateInstance" method existence, very
cool indeed!
 
C

colin

well I agree the struct v class is confusing thing to get used to.
and the pass by value/ref can be an awkward difference,
its not always the way you want it to be.

it would be expensive to replace all structs with classes,
not only that, the underlying library requires an array of structs for
various things.
especially for vertex data wich is itself a struct containing several
structs,
and has > 200,000 instances.

although I dont edit this amount of data with the property editor,
some of the structs are the same type, and this I have little control over,
or would be too inconvenient to have struct and class duplicated.

its hard to use TypedReference however, as you cant use it as a field,
nor can you return it as a function value, you cant even pass it as a ref
nor cast it to anything not even an object or value type.
so you have to use it in the same function, or sub function.

but it does allow you to modify a field of a struct within a struct.
all you need is an array of FieldInfo wich contain the list of nested
members,
and the final fieldinfo, and ofc an object such as a class that holds the
struct.
im not sure how it would work on an aray of structs.
although the class does actually hold a boxed copy of the struct in a
dictionary.

//recursively called
public UData SetField(object value,List<FieldInfo> subFfields)
{
....
if(...)
{
List<FieldInfo> fields = new List<FieldInfo>();
parentUdata = parent.SetField(null, fields);
if (fields.Count > 0)
{
fields.Reverse();
TypedReference typedReference =
TypedReference.MakeTypedReference(parentUdata.data, fields.ToArray());
fieldInfo.SetValueDirect(typedReference,value);
return null;
}
fieldInfo.SetValue(parentUdata.data,value);
return null;
}
...
}
Colin =^.^=

Marc Gravell said:
There is no special reason that primatives aren't affected, except
that they are immutable. That is the key point here:
the data in the structs does need to be edited though

There is a 99.8% change this is a mistake [according to the department
of invented statistics].

structs in C# (or rather: ValueType in .NET) are not the same as
structs in C++; they are not a "light weight object" or however else
people think of them... the difference is the copy vs reference
semantics. I don't know enough to recommend which... but I firmly
believe that you'll have a much easier time of things if you either a:
switch to classes instead of structs, or b: make the structs
immutable.

Marc
 

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