473,387 Members | 1,476 Online
Bytes | Software Development & Data Engineering Community
Post Job

Home Posts Topics Members FAQ

Join Bytes to post your question to a community of 473,387 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 4577
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 thread has been closed and replies have been disabled. Please start a new discussion.

Similar topics

17
by: Andreas Huber | last post by:
What follows is a discussion of my experience with .NET generics & the ..NET framework (as implemented in the Visual Studio 2005 Beta 1), which leads to questions as to why certain things are the...
4
by: Jethro Guo | last post by:
C++ template use constraint by signature,It's very flexible to programmer but complex for complier, and at most time programmer can not get clear error message from complier if error occur. C#...
8
by: JAL | last post by:
Here is my first attempt at a deterministic collection using Generics, apologies for C#. I will try to convert to C++/cli. using System; using System.Collections.Generic; using System.Text; ...
3
by: markww | last post by:
Hi, I have a wrapper around some 3rd party database library function. The pseudo code looks like the following - it is meant to open a table in a database, extract values from a table, then copy...
3
by: BombDrop | last post by:
Can any one help I have a method that will return a List to be bound as a datasource to a combobox see code for population below. I get the following error when i try to compile Error 29 ...
4
by: =?Utf-8?B?RXRoYW4gU3RyYXVzcw==?= | last post by:
Hi, I have written a generic method which does different things depending on the type of the parameter. I got it to work, but it seems really inelegant. Is there a better way to do this? In the...
26
by: raylopez99 | last post by:
Here is a good example that shows generic delegate types. Read this through and you'll have an excellent understanding of how to use these types. You might say that the combination of the generic...
3
by: Anders Borum | last post by:
Hi I need to invoke a generic method determined at runtime. The method has two arguments, a string and a generic type that is constrained to a struct: public void Add<T>(string key, T value)...
0
by: =?Utf-8?B?TW9ydGVuIFdlbm5ldmlrIFtDIyBNVlBd?= | last post by:
"Anders Borum" wrote: Hi Anders, I'm afraid the GetMethod() does not currently support filtering on generic parameters so you will have to loop through the existing methods using...
0
by: taylorcarr | last post by:
A Canon printer is a smart device known for being advanced, efficient, and reliable. It is designed for home, office, and hybrid workspace use and can also be used for a variety of purposes. However,...
0
by: Charles Arthur | last post by:
How do i turn on java script on a villaon, callus and itel keypad mobile phone
0
by: ryjfgjl | last post by:
If we have dozens or hundreds of excel to import into the database, if we use the excel import function provided by database editors such as navicat, it will be extremely tedious and time-consuming...
0
by: emmanuelkatto | last post by:
Hi All, I am Emmanuel katto from Uganda. I want to ask what challenges you've faced while migrating a website to cloud. Please let me know. Thanks! Emmanuel
1
by: nemocccc | last post by:
hello, everyone, I want to develop a software for my android phone for daily needs, any suggestions?
1
by: Sonnysonu | last post by:
This is the data of csv file 1 2 3 1 2 3 1 2 3 1 2 3 2 3 2 3 3 the lengths should be different i have to store the data by column-wise with in the specific length. suppose the i have to...
0
by: Hystou | last post by:
There are some requirements for setting up RAID: 1. The motherboard and BIOS support RAID configuration. 2. The motherboard has 2 or more available SATA protocol SSD/HDD slots (including MSATA, M.2...
0
marktang
by: marktang | last post by:
ONU (Optical Network Unit) is one of the key components for providing high-speed Internet services. Its primary function is to act as an endpoint device located at the user's premises. However,...
0
Oralloy
by: Oralloy | last post by:
Hello folks, I am unable to find appropriate documentation on the type promotion of bit-fields when using the generalised comparison operator "<=>". The problem is that using the GNU compilers,...

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.