Reflection and implicit operator in c# [duplicate] - c#

I have a class that pretends to be an int, so it has overloaded the various operators;
public class MyId
{
int value;
public virtual int Value
{
get { return this.value; }
set { this.value = value; }
}
public MyId(int value)
{
this.value = value;
}
public static implicit operator MyId(int rhs)
{
return new MyId(rhs);
}
public static implicit operator int(MyId rhs)
{
return rhs.Value;
}
}
However, when I use code like
PropertyInfo.SetValue(myObj, 13, null)
OR
MyId myId = 13;
int x = Convert.ToInt32(myId);
IConvertible iConvertible = x as IConvertible;
iConvertible.ToType(typeof(MyId), CultureInfo.CurrentCulture);
I get invalid cast. I'm puzzled, both calls seem to attempt to call convert on the int which will fail because int doesn't understand the type MyId (even though all the assignment operators are there). Any ideas of a workaround for this, I'm sure I must be missing something stupid?

Implicit conversions are a C# construct and are not available through reflection. Additionally, setting a field or property through reflection means that you must provide the appropriate type up front. You can attempt to circumvent this by using a custom TypeConverter (or some other means of custom conversion) to help convert your types at runtime prior to using reflection. Here's a rough example of a TypeConverter implementation.
public class MyIdTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value)
{
if (value is int)
return new MyId((int)value);
else if (value is MyId)
return value;
return base.ConvertFrom(context, culture, value);
}
}
Here's the type that we would be trying to set the Custom property on.
public class Container
{
[TypeConverter(typeof(MyIdTypeConverter))]
public MyId Custom { get; set; }
}
The code to call it would have to check the attribute and perform the conversion ahead of time, after which it could call SetValue.
var instance = new Container();
var type = typeof(Container);
var property = type.GetProperty("Custom");
var descriptor = TypeDescriptor.GetProperties(instance)["Custom"];
var converter = descriptor.Converter;
property.SetValue(instance, converter.ConvertFrom(15), null);

Related

Generic class accepts primitive type and string

How do i create a generic type which accepts only a type of Integer, Long and String.
I know we can restrict a type for a single class or by implementing an interface with below code
public class MyGenericClass<T> where T:Integer{ }
or to handle int,long but not string
public class MyGenericClass<T> where T:struct
Is it possible to create a generic which accepts only a type of Integer, Long and String?
You could potentially not have the constraints in the class declaration, but do some type-checking in a static constructor:
public class MyGenericClass<T>
{
static MyGenericClass() // called once for each type of T
{
if(typeof(T) != typeof(string) &&
typeof(T) != typeof(int) &&
typeof(T) != typeof(long))
throw new Exception("Invalid Type Specified");
} // eo ctor
} // eo class MyGenericClass<T>
Edit:
As Matthew Watson points out above, the real answer is "You can't and shouldn't". If your interviewer believes that to be incorrect, then you probably don't want to work there anyway ;)
I suggest that you use constructors to show what values are acceptable then store the value in an object. Like the following:
class MyClass
{
Object value;
public MyClass(int value)
{
this.value = value;
}
public MyClass(long value)
{
this.value = value;
}
public MyClass(string value)
{
this.value = value;
}
public override string ToString()
{
return value.ToString();
}
}

Is there any way to implicitly construct a type in C#?

I read of a useful trick about how you can avoid using the wrong domain data in your code by creating a data type for each domain type you're using. By doing this the compiler will prevent you from accidentally mixing your types.
For example, defining these:
public struct Meter
{
public int Value;
public Meter(int value)
{
this.Value = value;
}
}
public struct Second
{
public int Value;
public Second(int value)
{
this.Value = value;
}
}
allows me to not mix up meters and seconds because they're separate data types. This is great and I can see how useful it can be. I'm aware you'd still need to define operator overloads to handle any kind of arithmetic with these types, but I'm leaving that out for simplicity.
The problem I'm having with this approach is that in order to use these types I need to use the full constructor every time, like this:
Meter distance = new Meter(5);
Is there any way in C# I can use the same mode of construction that a System.Int32 uses, like this:
Meter distance = 5;
I tried creating an implicit conversion but it seems this would need to be part of the Int32 type, not my custom types. I can't add an Extension Method to Int32 because it would need to be static, so is there any way to do this?
You can specify an implicit conversion directly in the structs themselves.
public struct Meter
{
public int Value;
public Meter(int value)
{
this.Value = value;
}
public static implicit operator Meter(int a)
{
return new Meter(a);
}
}
public struct Second
{
public int Value;
public Second(int value)
{
this.Value = value;
}
public static implicit operator Second(int a)
{
return new Second(a);
}
}

How to provide conversion when using Reflection.SetValue?

I have a class that pretends to be an int, so it has overloaded the various operators;
public class MyId
{
int value;
public virtual int Value
{
get { return this.value; }
set { this.value = value; }
}
public MyId(int value)
{
this.value = value;
}
public static implicit operator MyId(int rhs)
{
return new MyId(rhs);
}
public static implicit operator int(MyId rhs)
{
return rhs.Value;
}
}
However, when I use code like
PropertyInfo.SetValue(myObj, 13, null)
OR
MyId myId = 13;
int x = Convert.ToInt32(myId);
IConvertible iConvertible = x as IConvertible;
iConvertible.ToType(typeof(MyId), CultureInfo.CurrentCulture);
I get invalid cast. I'm puzzled, both calls seem to attempt to call convert on the int which will fail because int doesn't understand the type MyId (even though all the assignment operators are there). Any ideas of a workaround for this, I'm sure I must be missing something stupid?
Implicit conversions are a C# construct and are not available through reflection. Additionally, setting a field or property through reflection means that you must provide the appropriate type up front. You can attempt to circumvent this by using a custom TypeConverter (or some other means of custom conversion) to help convert your types at runtime prior to using reflection. Here's a rough example of a TypeConverter implementation.
public class MyIdTypeConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context,
System.Globalization.CultureInfo culture,
object value)
{
if (value is int)
return new MyId((int)value);
else if (value is MyId)
return value;
return base.ConvertFrom(context, culture, value);
}
}
Here's the type that we would be trying to set the Custom property on.
public class Container
{
[TypeConverter(typeof(MyIdTypeConverter))]
public MyId Custom { get; set; }
}
The code to call it would have to check the attribute and perform the conversion ahead of time, after which it could call SetValue.
var instance = new Container();
var type = typeof(Container);
var property = type.GetProperty("Custom");
var descriptor = TypeDescriptor.GetProperties(instance)["Custom"];
var converter = descriptor.Converter;
property.SetValue(instance, converter.ConvertFrom(15), null);

C# converting string data representation to typed

Now Im writing a universal method for loading configuration data from XML. A lot of parameters in my case are stored in node attributes, so I decided to write a universal method for attribute reading:
private static T ReadAttribute<T>(XElement Element,string AttributeName)
{
var attrib = Element.Attribute(AttributeName);
if (attrib != null)
{
return attrib.Value; // off cource error is in this line !
}
else
{
return default(T);
}
}
This method should try to read attribute with specified name and if this attribute missed it should return default value for attribute type. Attribute type is specified by T.
As it shown in comment above my problem is that I cant universally convert string value into specific type. Actually I plan use int, double and two enum types as T.
What way I should act in this situation? How I should convert string value into T type?
Thanks in advance!
You can use the Convert.ChangeType. It does basically what you want. But it's a conversion not a cast, well, not just a cast.
return (T)Convert.ChangeType(attrib.Value, typeof(T), CultureInfo.InvariantCulture);
The reason why you can simply cast a string to some arbitrary type is that the type system doesn't allow that. However Convert.ChangeType returns object which could be any type and therefore the cast is allowed.
The CultureInfo.InvariantCulture is important becuase XML content isn't shouldn't be encoded/decoded using different cultures. The XmlConvert class should be used if working with XML however it doesn't have a handy generic method like XmlConvert.ChangeType.
The XAttribute class has many explicit user-defined casts that map to the XmlConvert class. However, you cannot simply use these with a unconstrained type parameter T and expect the same result.
To make matters worse, XML and Convert doesn't actually play nice. So if you're really serious about this you would write something like this to handle the conversions.
static T ConvertTo<T>(XAttribute attr)
{
object value;
switch (Type.GetTypeCode(typeof(T)))
{
case TypeCode.Boolean: value = XmlConvert.ToBoolean(attr.Value); break;
case TypeCode.Int32: value = XmlConvert.ToInt32(attr.Value); break;
case TypeCode.DateTime: value = XmlConvert.ToDateTime(attr.Value); break;
// Add support for additional TypeCode values here...
default:
throw new ArgumentException(string.Format("Unsupported destination type '{0}'.", typeof(T)));
}
return (T)value;
}
I would go with the TypeConverter stuff. It's basically a class that does conversions to/from values and cultures. The primary difference between a TypeConverter and Convert.ChangeType is that the later requires the IConvertible interface on the source type, while TypeConverters can work with any objects.
I've created a helper class for this, since I often store different configuration objects in xml-files. That's also why it's hardcoded to convert to/from CultureInfo.InvariantCulture.
public static class TypeConversion {
public static Object Convert(Object source, Type targetType) {
var sourceType = source.GetType();
if (targetType.IsAssignableFrom(sourceType))
return source;
var sourceConverter = TypeDescriptor.GetConverter(source);
if (sourceConverter.CanConvertTo(targetType))
return sourceConverter.ConvertTo(null, CultureInfo.InvariantCulture, source, targetType);
var targetConverter = TypeDescriptor.GetConverter(targetType);
if (targetConverter.CanConvertFrom(sourceType))
return targetConverter.ConvertFrom(null, CultureInfo.InvariantCulture, source);
throw new ArgumentException("Neither the source nor the target has a TypeConverter that supports the requested conversion.");
}
public static TTarget Convert<TTarget>(object source) {
return (TTarget)Convert(source, typeof(TTarget));
}
}
It's fully possible to create your own TypeConverter to handle system types, like System.Version (which doesnt implement IConvertible) to support conversions like from strings containing a version number ("a.b.c.d") to an actual Version object.
public class VersionTypeConverter : TypeConverter {
public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) {
if (sourceType == typeof(string))
return true;
return base.CanConvertFrom(context, sourceType);
}
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) {
var s = value as string;
if (s != null)
return new Version(s);
return base.ConvertFrom(context, culture, value);
}
public override bool CanConvertTo(ITypeDescriptorContext context, Type destinationType) {
if (destinationType == typeof(string))
return true;
return base.CanConvertTo(context, destinationType);
}
public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) {
var v = value as Version;
if (v != null && destinationType == typeof(string)) {
return v.ToString();
}
return base.ConvertTo(context, culture, value, destinationType);
}
}
To actually use this provider you need to registered it during application startup, using TypeDescriptor.AddProvider, passing in a custom TypeDescriptionProvider, and typeof(Version). This needs to return a custom CustomTypeDescriptor in the TypeDescriptorProvider.GetTypeDescriptor method, and the descriptor needs to override GetConverter to return a new instance of VersionTypeConverter. Easy. ;)
Built-in methods won't help if T is a type defined by yourself. Let's say the xml looks like:
//some other segments
<Book Name="Good book" Price="20" Author="Jack" />
And you T is the class Book that looks like:
class Book
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Author { get; set; }
//maybe some other properties
}
There is no magic to convert the XElement to an instance of Book automatically, you need to implement it yourself. An easy and general implementation is something like this:
interface IXElementConvertible
{
void LoadFrom(XElement element);
}
class Book : IXElementConvertible
{
public string Name { get; set; }
public decimal Price { get; set; }
public string Author { get; set; }
public void LoadFrom(XElement element)
{
this.Name = element.Attribute("Name").Value;
//blabla
}
}
And you need to modify your method:
private static T ReadAttribute<T>(XElement Element,string AttributeName)
where T : IXElementConvertible, new()
{
T t = new T();
t.LoadFrom(element);
//just an example here, not the complete implementation
}
I think you should do the following check in you code:
if (attrib.Value is T)
{
...
}

C# : How does this work : Unit myUnit = 5;

I just noticed that you can do this in C#:
Unit myUnit = 5;
instead of having to do this:
Unit myUnit = new Unit(5);
Does anyone know how I can achieve this with my own structs? I had a look at the Unit struct with reflector and noticed the TypeConverter attribute was being used, but after I created a custom TypeConverter for my struct I still couldn't get the compiler to allow this convenient syntax.
You need to provide an implicit conversion operator from int to Unit, like so:
public struct Unit
{ // the conversion operator...
public static implicit operator Unit(int value)
{
return new Unit(value);
}
// the boring stuff...
private readonly int value;
public int Value { get { return value; } }
public Unit(int value) { this.value = value; }
}
You need to provide a cast operator for the class that takes an Int32.

Categories