How to use C# generics to simplify access to WinRT settings? - c#

In WinRT, settings are stored as objects which means that you end up doing a lot of casting to get back to the type you want. Since that seems to map onto one of the reasons why generics were added to C#, I've been trying to simplify my code so that I can do something like:
public string LastRunVersion
{
get
{
return GetLocalSettingsValue<String>("LastRunVersion", null);
}
set
{
SetLocalSettingsValue("LastRunVersion", value);
}
}
The problem I'm having is with the signature for SetLocalSettingsValue. I tried:
private T GetLocalSettingsValue<T>(string tag, T defaultValue) where T:Object
but Object isn't allowed because it isn't a valid constraint. I know that I only store booleans and strings, so I then tried:
private T GetLocalSettingsValue<T>(string tag, T defaultValue) where T:String, bool
but the compiler says that "A type used as a constraint must be an interface, a non-sealed class or a type parameter".
What do I need to do with the definition in order to allow me to use string and bool?
Thanks.

Implementing two different methods, as Sriram suggests, does seem like the simplest solution for the immediate concern. That said, your generic approach is viable, has precedent, and will make it much easier to extend the code in the future.
In fact, what you tried was nearly correct. The main thing is that you don't actually need a constraint at all. The method declaration is fine without it, and though you don't show the method body, as long as all you need to do is cast some object reference to the type T, that will work:
private T GetLocalSettingsValue<T>(string tag, T defaultValue)
{
object value;
// initialize/retrieve the value somehow
// Check for value present, return default if missing, cast otherwise
return value != null ? (T)value : defaultValue;
}
In fact, since you are passing a default value, type inference will allow you to omit the type parameter in some cases. For example:
public string LastRunVersion
{
get { return GetLocalSettingsValue("LastRunVersion", (string)null); }
}
If you had a non-null default value, the above would be more interesting. :) With the null value, you have to cast it to string just for the compiler to know what the correct type is, which is practically the same as just providing the type parameter. But if you were passing a string literal or the value of a string variable, the type would be clear and the name of the type would not need to be provided at all (not even as a cast).
More interesting is the bool scenario:
public string LastRunVersion
{
get { return GetLocalSettingsValue("SomeBooleanSetting", false); }
}
Here, the literal has a clear type, and so you don't need to provide the type name in any form.
Finally, note that C# does have the idea of default values for types. If you want to support non-null, non-zero default values, then your current approach is good. However, if your defaults are always going to be things like null, false, or 0 (e.g. for an int, should you ever need to store something like that), then you don't need the default parameter at all:
private T GetLocalSettingsValue<T>(string tag)
{
object value;
// initialize/retrieve the value somehow
// Check for value present, return default if missing, cast otherwise
return value != null ? (T)value : default(T);
}
Any reference type will use null as the default. For value types, you will get whatever the value would be if you created an instance using the parameterless constructor (all value types have a parameterless constructor). Numeric types all default to their version of 0, bool defaults to false, etc.
Naturally, in that case you will always have to provide the type parameter, since there aren't any arguments from which the type parameter could be inferred.

Related

C# object{int} not equal to object{int} [duplicate]

Today I stumbled upon an interesting bug I wrote. I have a set of properties which can be set through a general setter. These properties can be value types or reference types.
public void SetValue( TEnum property, object value )
{
if ( _properties[ property ] != value )
{
// Only come here when the new value is different.
}
}
When writing a unit test for this method I found out the condition is always true for value types. It didn't take me long to figure out this is due to boxing/unboxing. It didn't take me long either to adjust the code to the following:
public void SetValue( TEnum property, object value )
{
if ( !_properties[ property ].Equals( value ) )
{
// Only come here when the new value is different.
}
}
The thing is I'm not entirely satisfied with this solution. I'd like to keep a simple reference comparison, unless the value is boxed.
The current solution I am thinking of is only calling Equals() for boxed values. Doing a check for a boxed values seems a bit overkill. Isn't there an easier way?
If you need different behaviour when you're dealing with a value-type then you're obviously going to need to perform some kind of test. You don't need an explicit check for boxed value-types, since all value-types will be boxed** due to the parameter being typed as object.
This code should meet your stated criteria: If value is a (boxed) value-type then call the polymorphic Equals method, otherwise use == to test for reference equality.
public void SetValue(TEnum property, object value)
{
bool equal = ((value != null) && value.GetType().IsValueType)
? value.Equals(_properties[property])
: (value == _properties[property]);
if (!equal)
{
// Only come here when the new value is different.
}
}
( ** And, yes, I know that Nullable<T> is a value-type with its own special rules relating to boxing and unboxing, but that's pretty much irrelevant here.)
Equals() is generally the preferred approach.
The default implementation of .Equals() does a simple reference comparison for reference types, so in most cases that's what you'll be getting. Equals() might have been overridden to provide some other behavior, but if someone has overridden .Equals() in a class it's because they want to change the equality semantics for that type, and it's better to let that happen if you don't have a compelling reason not to. Bypassing it by using == can lead to confusion when your class sees two things as different when every other class agrees that they're the same.
Since the input parameter's type is object, you will always get a boxed value inside the method's context.
I think your only chance is to change the method's signature and to write different overloads.
How about this:
if(object.ReferenceEquals(first, second)) { return; }
if(first.Equals(second)) { return; }
// they must differ, right?
Update
I realized this doesn't work as expected for a certain case:
For value types, ReferenceEquals returns false so we fall back to Equals, which behaves as expected.
For reference types where ReferenceEquals returns true, we consider them "same" as expected.
For reference types where ReferenceEquals returns false and Equals returns false, we consider them "different" as expected.
For reference types where ReferenceEquals returns false and Equals returns true, we consider them "same" even though we want "different"
So the lesson is "don't get clever"
I suppose
I'd like to keep a simple reference comparison, unless the value is boxed.
is somewhat equivalent to
If the value is boxed, I'll do a non-"simple reference comparison".
This means the first thing you'll need to do is to check whether the value is boxed or not.
If there exists a method to check whether an object is a boxed value type or not, it should be at least as complex as that "overkill" method you provided the link to unless that is not the simplest way. Nonetheless, there should be a "simplest way" to determine if an object is a boxed value type or not. It's unlikely that this "simplest way" is simpler than simply using the object Equals() method, but I've bookmarked this question to find out just in case.
(not sure if I was logical)

Pass Type as parameter and check

I want to build a method that accepts parameter as Type like
void M1(Type t)
{
// check which type it is
}
and call it like
M1(typeof(int));
I have no idea how to check type in method body.
I have tried
if (t is double)
But it is giving warning
The given expression never provided type (double)
Please help me for checking the type of parameter.
If you want to check for an exact type, you can use:
if (t == typeof(double))
That's fine for double, given that it's a struct, so can't be inherited from.
If you want to perform a more is-like check - e.g. to check whether a type is compatible with System.IO.Stream - you can use Type.IsAssignableFrom:
if (typeof(Stream).IsAssignableFrom(t))
That will match if t is System.IO.MemoryStream, for example (or if it's System.IO.Stream itself).
I always find myself having to think slightly carefully to work out which way round the call goes, but the target of the call is usually a typeof expression.
You can try
if(t == typeof(double))
or
if (typeof(double).IsAssignableFrom(t))
or
if(t == default(double).GetType())
or
if(t.Name == "Double")
Personally i prefer the first option
Have a look at IsAssignableFrom, which determines whether an instance of a specified type can be assigned to the current type instance.
public void M<T>(T value)
{
if (typeof(T).IsAssignableFrom(typeof(double)))
Console.Write("It's a double");
}
It returns true if the given parameter:
represents the same type.
is derived either directly or indirectly from the current instance.
is a generic type parameter, and the current instance represents one of the constraints of the parameter.
represents a value type, and the current instance represents Nullable (Nullable(Of paramerter) in Visual Basic).

Generic fails when converting int to bool

I've the following method to search in Registry (don't worry, it's a test using generics):
private static T GetValue<T>(RegistryKey key, string name)
{
using (key)
{
return (T)key.GetValue(name);
}
}
But when using type : Nullable<bool> it fails because of an invalid cast.
(I tried on numeric keys only)
What I want is to have null if key not exists, false if 0 is stored in, or true else.
How can I achieve that?
Thanks !
because generics cannot utilize type-specific behavior, casts to or from a generic type parameter are assumed to be upcasts/downcasts.
a downcast fails here, you need a conversion.
you can try the Convert class, but better to make the type param match the registry type.
If you want such specific behavior (null if null, false if 0, true otherwise) then what would your generic method return if T is not a bool??
Maybe you can stop to use magic cast code. And cast explicitly, so you'll always got what you want.

Generic property- how to specify the type at run time

I was reading a question on making a generic property, but I'm a little confused by the last example from the first answer (I've included the relevant code below):
You have to know the type at compile
time. If you don't know the type at
compile time then you must be storing
it in an object, in which case you can
add the following property to the Foo
class:
public object ConvertedValue {
get {
return Convert.ChangeType(Value, Type);
}
}
That's seems strange: it's converting the value to the specified type, but it's returning it as an object when the value was stored as an object. Doesn't the returned object still require un-boxing? If it does, then why bother with the conversion of the type?
I'm also trying to make a generic property whose type will be determined at run time:
public class Foo
{
object Value {get;set;}
Type ValType{get;set;}
Foo(object value, Type type)
{ Value = value; ValType = type; }
// I need a property that is actually
// returned as the specified value type...
public object ConvertedValue {
get {
return Convert.ChangeType(Value, ValType);
}
}
}
Is it possible to make a generic property? Does the return property still require unboxing after it's accessed?
Note: I don't want to make Foo generic because I want Foo to contain values of different types and I want to put various Foos into a collection. In other words, I want to have a collection that holds different types of objects.
Convert.ChangeType() decides on the type at run time. The parameter you give can be calculated at run time and can be something the compiler can't know on compilation.
This is why it must return a generic object and not a specific type.
It still converts the type - for example from int to double. The compilation time type is object but the run time type changes. If you run GetType() on that object you will get the actual run time type.
When you convert a value type to a reference type you get boxing and the other way around you get un-boxing - so this depends on the types you use.

String casts in .NET

Why is there so may ways to convert to a string in .net? The ways I have seen are .ToString, Convert.ToString() and (string). What is the Difference.
Convert.ToString(obj)
Converts the specified value to its equivalent String representation. Will return String.Empty if specified value is null.
obj.ToString()
Returns a String that represents the current Object. This method returns a human-readable string that is culture-sensitive. For example, for an instance of the Double class whose value is zero, the implementation of Double.ToString might return "0.00" or "0,00" depending on the current UI culture. The default implementation returns the fully qualified name of the type of the Object.
This method can be overridden in a derived class to return values that are meaningful for that type. For example, the base data types, such as Int32, implement ToString so that it returns the string form of the value that the object represents. Derived classes that require more control over the formatting of strings than ToString provides must implement IFormattable, whose ToString method uses the current thread's CurrentCulture property.
(string)obj
It's a cast operation, not a function call. Use it if you're sure that the object is of type string OR it has an implicit or explicit operator that can convert it to a string. Will return null if the object is null AND of type String or of type which implements custom cast to string operator. See examples.
obj as string
Safe cast operation. Same as above, but instead of throwing an exception it will return null if cast operation fails.
Hint: Don't forget to use CultureInfo with obj.ToString() and Convert.ToString(obj)
Example:
12345.6D.ToString(CultureInfo.InvariantCulture); // returns 12345.6
12345.6D.ToString(CultureInfo.GetCultureInfo("de-DE")); // returns 12345,6
Convert.ToString(12345.6D, CultureInfo.InvariantCulture); // returns 12345.6
Convert.ToString(12345.6D, CultureInfo.GetCultureInfo("de-DE")); // 12345,6
Convert.ToString(test); // String.Empty, "test" is null and it's type
// doesn't implement explicit cast to string oper.
Convert.ToString(null); // null
(string) null; // null
(string) test; // wont't compile, "test" is not a string and
// doesn't implement custom cast to string operator
(string) test; // most likely NullReferenceException,
// "test" is not a string,
// implements custom cast operator but is null
(string) test; // some value, "test" is not a string,
// implements custom cast to string operator
null as string; // null
Here is an example of custom cast operator:
public class Test
{
public static implicit operator string(Test v)
{
return "test";
}
}
.ToString() can be called from any object. However, if the type you call it on doesn't have a good implementation the default is to return the type name rather than something meaningful about the instance of that type. This method is inherited from the base Object type, and you can overload it in your own types to do whatever you want.
(string) is a cast, not a function call. You should only use this if the object you need already is a string in some sense, or if you know there is a good implicit conversion available (like from int). This will throw an exception is the object cannot be converted (including when the object is null)
as string is another way to write (string), but it differs in that it returns null rather than throwing an exception if the cast fails.
Convert.ToString() attempts to actually convert the argument into a string. This is the best option if you don't really know much about the argument. It can be slow because it has to do a lot of extra work to determine what kind of results to return, but that same work also makes it the most robust option when you don't know very much about the argument. If nothing else is available, it will fall back to calling the argument's .ToString() method.
String.Format The string class' .Format method can also be used to convert certain types to strings, with the additional advantage that you have some control over what the resulting string will look like.
Serialization This is a little more complicated, but .Net includes a couple different mechanisms for converting objects into a representation that can be safely stored and re-loaded from disk or other streaming mechanism. That includes a binary formatter, but most often involves converting to a string in some format or other (often xml). Serialization is appropriate when you want to later convert the your string back into it's originating type, or if you want a complete representation of a complex type.
Convert.ToString() will return an empty string if the object is null .ToString and (String) will throw an exception. Convert.ToString will internally call .ToString() if the value is null it will return an empty String.
object.ToString() is the most basic way of retrieving a string representation of an object, and can be specifically implemented by the object.
Convert.ToString() expands on that and provides some specific overloads for primitive types (char, byte, int, double, etc.) that allow for some more type-specific functionality (like base conversion, for example)
(string) is a casting operator, and will only work if the type is either a string or has an implicit or explicit operator that can convert it to a string. Otherwise you'll get an InvalidCastException
Don't forget as string
ToString() is a method of object, and it will always work on a non-null reference, so you'll get something, but whether that something is what you want, is a different story.
Convert.ToString() will yield the same result in most cases, but isn't as flexible as Object.ToString() as you can't pass custom formatting rules.
(string) will cast your object to string, and if it isn't a string then you'll get an InvalidCastException().
Think.
ToString is a virtual method, and each type can implement it however it wants. Also System.Object provides default implementations so that it always succeeds.
Convert.ToString works only with nulls as well and allows you to use IFormat provier as noted in the comment.
Casting to string requires object to implement casting operator. Again, types can implement it however they like, but most types do not, so you may get an exception here.
Use .ToString as your best option.
.ToString() is an instance method which asks the object for its string representation. When the object is null, this will throw a exception.
(string) is a cast to the string type, which isn't a very good idea in most cases except for simple data types, since it can break (throw an exception) when it's null or an invalid cast
Convert.ToString() does a bit more checking than a simple cast, giving a more robust alternative to the cast. It will return the empty string when the object is null.
Not to nitpick but null is a valid value for a String object. Therefore (string) null does not throw any exceptions. Try it for yourselves:
using System;
namespace Test
{
class Program
{
public static void Main(string[] args)
{
string s = (string) null;
Console.WriteLine(s);
}
}
}

Categories

Resources