471,350 Members | 1,741 Online
Bytes | Software Development & Data Engineering Community
Post +

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 471,350 software developers and data experts.

Constraining a generic method to struct and string

Hi!

While implementing a property manager (that supports key / value pairs), I
was wondering how to constrain T to a struct or string type. Basically, I
guess what I'm looking for is the common denominator for structs and strings
and while looking through the SDK I only noticed the IEquatable<T>
interface.

So I implemented the class with methods such as the following, but I'm aware
that this is not the right approach.

// Initial implementation
public void Add<T>(string key, T value) where T : IEquatable<T>
{
this.AddProperty(key, value);
}

// Proposed implementation
public void Add<T>(string key, T value) where T : IEquatable<T>
{
if (! (value is ValueType) || ! (value is string)) { throw new
InvalidOperationException("message"); }

this.AddProperty(key, value);
}

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)

Jun 27 '08 #1
5 4413
Anders Borum wrote:
While implementing a property manager (that supports key / value pairs),
I was wondering how to constrain T to a struct or string type.
Though it's not the same as implementing a single method, you can do it with
overloads:

public void Foo<T>(T value) where T : struct { ... }
public void Foo(string value) { ... }

However, I would be very surprised if you actually needed such a construct,
so it's worth taking a closer look.
Basically, I guess what I'm looking for is the common denominator for
structs and strings and while looking through the SDK I only noticed the
IEquatable<Tinterface.
There is no common denominator for structs and strings, other than Object,
and it's unclear what exactly you're after. In what circumstances would it
be meaningful to restrict types to structs or strings? Keep in mind that a
struct is really just a sealed class with value type semantics, while a
string is an immutable reference type. They're not at all comparable.

What exactly do you require of keys that makes you think you need to
restrict them to strings and structs?
So I implemented the class with methods such as the following, but I'm
aware that this is not the right approach.

// Initial implementation
public void Add<T>(string key, T value) where T : IEquatable<T>
{
this.AddProperty(key, value);
}

// Proposed implementation
public void Add<T>(string key, T value) where T : IEquatable<T>
{
if (! (value is ValueType) || ! (value is string)) { throw new
InvalidOperationException("message"); }
What's "message"? :-)
this.AddProperty(key, value);
}
Again, what are you trying to do here? Why do you think you need to use
value types and strings? If you want the ability to copy an object,
constrain the type to ICloneable and use .Clone(). If you need to compare
them for equality, then IEquatable<is indeed a good choice. It seems like
you're imposing an arbitrary restriction that doesn't gain you anything.

--
J.
http://symbolsprose.blogspot.com
Jun 27 '08 #2
On Mon, 14 Apr 2008 13:48:20 -0700, Anders Borum <an****@sphereworks.dk>
wrote:
Hi!

While implementing a property manager (that supports key / value pairs),
I was wondering how to constrain T to a struct or string type.
Basically, I guess what I'm looking for is the common denominator for
structs and strings and while looking through the SDK I only noticed the
IEquatable<Tinterface.

So I implemented the class with methods such as the following, but I'm
aware that this is not the right approach.
It seems to me that your proposed implementation, in which you check the
actual type, is about as good as you're going to get. Except that I
wouldn't bother with the constraint.

I don't think structs are provided a default IEquatable<Tinterface so
not all structs would necessarily have that interface anyway. And even if
they did, there's nothing about your description that suggests that you
really care about the members of the IEquatable<Tinterface anyway. So
the constraint doesn't really represent what you're trying to do, and
would in fact be misleading to anyone trying to figure out the method's
declaration.

Pete
Jun 27 '08 #3
>*And even if *
they did, there's nothing about your description that suggests that you *
really care about the members of the IEquatable<Tinterface anyway.
Absolutely; but I know where you're coming from - most times people
ask about "struct and strings" it is to detect changes in values (for
things such as INotifyPropertyChanged). Actually, in many cases I've
found that the IEquatable<Tmethod just gets in the way - especially
if generics are involved (constraints accumulate, which can mean you
have piles of constraints). I generally use
EqualityComparer<T>.Default instead of an explicit IEquatable<T>
constraint - this works the same (slightly different IL of course),
but doesn't need the constraint, and also works (via Equals) for
legacy types that don't implement IEquatable<T>.

Marc
Jun 27 '08 #4
Regardless, for unknown types I'd still require the class / struct to
provide a Parse(string) method that can be called via reflection when
deserializing the values.
Can I recommend XmlConvert, i.e.

XmlConvert.To[...] (deserialize), or XmlConvert.ToString (serialize)

This will use suitable xml formats, and (IIRC) invariant culture.
As a secondary to XmlConvert, TypeDescriptor is probably a next best
thing - i.e.

TypeDescriptor.GetConverter(typeof(T)).Convert[To|From]InvariantString

Also - watch for a typo (Int63) - that would break things pretty
quickly...

Of course, another approach would be to use XmlSerializer or
DataContractSerializer, which will cope with more scenarios than I
care to have to list.

Marc
Jun 27 '08 #5
Hi Marc

Sorry for the delay (I was busy writing APIs). Thanks for pointing out the
typo!

I decided to go with the XmlConvert because of the locale independence as I
couldn't find any reason to reproduce the implementation in that class just
to save a few method calls. I would have liked a generic
XmlConvert.ToString(object) signature, but I guess the implementation I came
up with is quite good in terms of maintenance and performance (the latter is
great).
Of course, another approach would be to use XmlSerializer or
DataContractSerializer, which will cope with more scenarios than I
care to have to list.
Again, the PropertyManager was intended as a storage for generic
configuration values / list of values. Not entire serialized graphs
(although it's definately supported by means of using a string configuration
value).

Here's the "CmsXmlConverter" class I ended up implementing - it may be of
use to others. As can be read from above, I'd have liked an
XmlConvert.ToString(object) method, but decided to skip the reflection
(emit) approch and wrote a switched implementation instead (which works very
well, I might add).

If you've got additional comments, I'm always very interested - and thanks
for the information so far!

public static string GetString(string typeName, object value)
{
if (value == null) { return null; }

switch (typeName)
{
case "System.String": { return (string) value; }
case "System.Guid": { return XmlConvert.ToString((Guid) value); }
case "System.DateTime": { return XmlConvert.ToString((DateTime) value,
XmlDateTimeSerializationMode.Utc); }
case "System.DateTimeOffset": { return
XmlConvert.ToString((DateTimeOffset) value); }
case "System.TimeSpan": { return XmlConvert.ToString((TimeSpan)
value); }
case "System.Bool": { return XmlConvert.ToString((bool) value); }
case "System.Byte": { return XmlConvert.ToString((byte) value); }
case "System.Char": { return XmlConvert.ToString((char) value); }
case "System.Decimal": { return XmlConvert.ToString((decimal) value); }
case "System.Double": { return XmlConvert.ToString((double) value); }
case "System.Single": { return XmlConvert.ToString((float) value); }
case "System.Int16": { return XmlConvert.ToString((short) value); }
case "System.Int32": { return XmlConvert.ToString((int) value); }
case "System.Int64": { return XmlConvert.ToString((long) value); }
case "System.SByte": { return XmlConvert.ToString((sbyte) value); }
case "System.UInt16": { return XmlConvert.ToString((ushort) value); }
case "System.UInt32": { return XmlConvert.ToString((uint) value); }
case "System.UInt64": { return XmlConvert.ToString((ulong) value); }
default: { return value.ToString(); }
}
}

public static object GetObject(string typeName, string value)
{
if (string.IsNullOrEmpty(value)) { return null; }

return GetParser(typeName)(value);
}

public static Func<string, objectGetParser(Type type)
{
return GetParser(type.FullName);
}

public static Func<string, objectGetParser(string typeName)
{
switch (typeName)
{
case "System.String": { return (p) =p; }
case "System.Guid": { return (p) =XmlConvert.ToGuid(p); }
case "System.DateTime": { return (p) =XmlConvert.ToDateTime(p,
XmlDateTimeSerializationMode.Utc); }
case "System.DateTimeOffset": { return (p) =>
XmlConvert.ToDateTimeOffset(p); }
case "System.TimeSpan": { return (p) =XmlConvert.ToTimeSpan(p); }
case "System.Bool": { return (p) =XmlConvert.ToBoolean(p); }
case "System.Byte": { return (p) =XmlConvert.ToByte(p); }
case "System.Char": { return (p) =XmlConvert.ToChar(p); }
case "System.Decimal": { return (p) =XmlConvert.ToDecimal(p); }
case "System.Double": { return (p) =XmlConvert.ToDouble(p); }
case "System.Single": { return (p) =XmlConvert.ToSingle(p); }
case "System.Int16": { return (p) =XmlConvert.ToInt16(p); }
case "System.Int32": { return (p) =XmlConvert.ToInt32(p); }
case "System.Int64": { return (p) =XmlConvert.ToInt64(p); }
case "System.SByte": { return (p) =XmlConvert.ToSByte(p); }
case "System.UInt16": { return (p) =XmlConvert.ToUInt16(p); }
case "System.UInt32": { return (p) =XmlConvert.ToUInt32(p); }
case "System.UInt64": { return (p) =Convert.ToUInt64(p); }
default:
{
Type type = Type.GetType(typeName);
MethodInfo info = type.GetMethod("Parse", new Type[] {
typeof(string) });

if (info == null) { throw new
InvalidOperationException(string.Format("Unable to find the Parse(string
value) method for type: {0}.", type.FullName)); }

return (p) =info.Invoke(null, new object[] { p });
}
}
}

--
With regards
Anders Borum / SphereWorks
Microsoft Certified Professional (.NET MCP)

Jun 27 '08 #6

This discussion thread is closed

Replies have been disabled for this discussion.

Similar topics

4 posts views Thread by Jethro Guo | last post: by
8 posts views Thread by JAL | last post: by
3 posts views Thread by markww | last post: by
3 posts views Thread by BombDrop | last post: by
4 posts views Thread by =?Utf-8?B?RXRoYW4gU3RyYXVzcw==?= | last post: by
26 posts views Thread by raylopez99 | last post: by
reply views Thread by =?Utf-8?B?TW9ydGVuIFdlbm5ldmlrIFtDIyBNVlBd?= | last post: by

By using Bytes.com and it's services, you agree to our Privacy Policy and Terms of Use.

To disable or enable advertisements and analytics tracking please visit the manage ads & tracking page.