In my current project, i have a Value<T> class.
Currently T can be anything, but we must support Null values, so currently we use it in two forms:
Value<string> or Value<int?>
Can I in any way create a Value class that would allow me to specify Value<string> or Value<int>, but where the effect is that Value holds T for classes, but holds T? for structs?
The goal is to avoid the case where a developer specifies Value<int> and we later have problems, because we dont handle Null values properly.
Ie, id like Compiler suport for avoiding errors.
Can I in any way create a Value class that would allow me to specify Value<string> or Value<int>, but where the effect is that Value holds T for classes, but holds T? for structs?
No, because you cannot have "or" logic in generic type constraints.
To achieve "or" logic in generic type constraints, you would have to create two separate generic classes, each with their own generic type constraint. Note that these two classes could inherit from a shared generic base class which has no type constraint at all.
If that base type is abstract, then you can be sure that consumers must have passed one of the derived classes' type constraint check (assuming no one added some other derived classes)
It would be more appropriate to change your expectation instead of trying to make it the way you currently want to.
but we must support null values
If instead of null, you use default(T), then the problem is resolved.
For class types, default(MyClass) effectively resolves to null
For structs, default(MyStruct) returns a struct whose properties all contain their default values
default(MyStruct) is the common way to use the concept of "nullability" without having to use a literal null value.
A usage example:
public class Container<T>
{
private readonly Random _random = new Random();
public T DoSomething(T input)
{
//always returns "nothing"
return default(T);
}
}
public class MyClass
{
public int Value { get; set; }
}
public struct MyStruct
{
public int Value { get; set; }
}
public class Test
{
public bool ContainerReturnsNothing<T>(T input)
{
var container = new Container<T>();
var output = container.DoSomething(input);
return output.Equals(default(T));
}
public void TestAClassAndAStruct()
{
ContainerReturnsNothing(new MyClass() {Value = 1}); // true
ContainerReturnsNothing(new MyStruct() {Value = 1}); // true
}
}
In the TestAClassAndAStruct method you can see that this generic call stack works exactly the same regardless of whether you use a class or a struct.
If you change the container to return input; then ContainerReturnsNothing will return false in both cases.
One thing you may need to be aware of is that a "nothing" struct cannot be differentiated from a struct that was created but whose properties all happen to have default values. If a struct whose properties all happen to have default values, is a meaningful (i.e. not-nothing) struct value in your case, then this is an issue.
A simple example here is int, whose default(int) resolves to 0. If 0 is a meaningful value, then it is therefore not nothing, which means that you cannot use 0 to represent nothingness.
But you can work around that issue by adding a property which is forced to contain a non-default value when a constructor is executed:
public struct MyStruct
{
public bool IsConstructed { get; } // default false
public int Value { get; }
public MyStruct(int myValue)
{
this.IsConstructed = true;
this.Value = myValue;
}
}
default(MyStruct) will always have IsConstructed set to false.
Every instantiated struct will always have IsConstructed set to true.
This avoid the issue since they will never be equal to one another. In other words:
var myStruct = new MyStruct(0);
var isNull = myStruct.Equals(default(MyStruct));
isNull is false because myStruct and default(MyStruct) contain a different value for IsConstructed (true and false, respectively).
If you run this same check using the MyStruct class from the original example, isNull would be true because all the properties match, i.e. myStruct and default(MyStruct) both have a Value property set to 0.
Related
I'm implementing a C# variant of Haskell's Maybe and came across a weird issue where null and default has different implication on the value returned from an implicit conversion.
public class TestClass
{
public void Test()
{
Maybe<string> valueDefault = default;
Maybe<string> valueNull = null;
Maybe<string> valueFoobar = "foobar";
Console.WriteLine($"Default: (Some {valueDefault.Some}, None {valueDefault.None}");
Console.WriteLine($"Null: (Some {valueNull.Some}, None {valueNull.None}");
Console.WriteLine($"Foobar: (Some {valueFoobar.Some}, None {valueFoobar.None}");
}
}
public struct Maybe<T>
{
public T Some { get; private set; }
public bool None { get; private set; }
public static implicit operator Maybe<T>(T value)
{
return new Maybe<T>() {
Some = value,
None = value == null
};
}
}
The output being:
Default: (Some , None False)
Null: (Some , None True)
Foobar: (Some foobar, None False)
I was expecting both valueDefault and valueNull to be equal. But seems that null is converted while default isn't. I fixed the issue by replacing None with HasSome with a reversed boolean condition, but still the question remains.
Why is null and default treated differently?
Every type has a default value, including Maybe<T>. See this page for a list.
Maybe<string> valueDefault = default; will assign the default value of Maybe<string> to valueDefault. What's the default value of Maybe<string>? According to that page, since Maybe<string> is a struct, its default value is:
The value produced by setting all value-type fields to their default values and all reference-type fields to null.
So it's an instance of Maybe<string> with Some being null and None being false. false is the default value of bool.
The compiler doesn't try to use the default value of string, since that requires a further conversion to Maybe<string>. If it can just use the default value of Maybe<string>, why go the extra trouble, right?
You can force it to though:
Maybe<string> valueDefault = default(string);
null, on the other hand, gets converted to Maybe<string> because null is not a valid value of Maybe<string> (structs can't be null!), so the compiler deduces that you must mean null as string, and does the implicit conversion.
You might know this already, but you seem to be reinventing Nullable<T>
default always fills the memory of the struct with zero bytes. null is not a valid value for a value type, so the compiler discovers the implicit (Maybe<string>)(string)null cast.
Perhaps you could replace with;
public struct Maybe<T>
{
public T Some { get; private set; }
public bool None => Some == null;
...
I have a class that has several constructors like this:
var Value; //Does not work
public MyClass(int val, byte[] data)
{
//Assign Value
}
public MyClass(short val, byte[] data)
{
//Assign Value
}
public MyClass(bool val, byte[]data)
{
//Assign Value
}
//......More down
Is it possible to create a Property called Value of the same type as the parameters passed in the constructors? The reason is that i am passing a byte[] coming from a TCP Stream and i need to know the type so i know what Bitconverter function to call.
The alternative I was thinking was to have the caller pass an enum with the type in the constructor as well
Keep in mind that var does not mean generic or loosely typed. It is simply syntactic sugar that infers a type based on the value you provide.
However all is not lost, C# has a feature called generics that will allow you to define a type when you instantiate the class allowing you to have a single class that could can handle multiple "types" in some way. Using generics should give you the result you want.
First thing you need to do is make your class generic:
public class MyClass<T>
{
public MyClass(T initialValue)
{
Value = initialValue;
}
public T Value {get; set;}
}
Then you can create instances of your class like this:
var myObj = new MyClass<bool>(true); // "myObj.Value" is a bool
var myObj2 = new MyClass<int>(13); // "myObj2.Value" is an int
Fiddle here
Sometimes I need value objects without fields (message headers, schemas, etc.), for example:
abstract class RequestHeader
{
}
sealed class FirstRequestHeader : RequestHeader
{
}
I use them in methods like:
partial class Server
{
private readonly IReadOnlyDictionary<RequestHeader, Action<object>> requestMap;
public void ProcessRequest(RequestHeader header, object request)
{
requestMap[header](request);
}
}
In this case default implementation of GetHashCode and Equals methods totally fits my needs, because I can use singletons.
But since FirstRequestHeader is an immutable value object I want it to behave like a real value object:
var a = new FirstRequestHeader();
var b = new FirstRequestHeader();
Console.WriteLine(a == b &&
a.Equals(b) &&
a.GetHashCode() == b.GetHashCode()); // False, but should be True
Overriding == operator and Equals method is easy.
But what is correct or recommended way of overriding GetHashCode method in this case?
I can expect some answers (all with some drawbacks):
hardcode constant hashcode for each type
generate one each execution and save it in a static field
use type's hashcode through GetType method
avoid empty objects (add a field)
But no assumption was confirmed by searching
So, what would you do?
If there is no data associated with the class then make only one instance.
sealed class FirstRequestHeader : RequestHeader
{
public static readonly FirstRequestHeader Value = new FirstRequestHeader();
private FirstRequestHeader()
{
}
}
hardcode constant hashcode for each type
If you want two "identical" objects to be treated as equal (and with no fields or your instances are identical), this is definitely the way to go.
Adding a new field, which I assume you won't modify in any meaningful way results in the same, just overcomplicates this. Same can be said for the other two approaches.
Please note that you can choose any value - you do not need to worry about possible hash code collisions between different types, so keep it simple.
If you want all instances to have the same hashcode without using constants you could also use the hashcode of the type:
public class FirstRequestHeader
{
public override int GetHashCode()
{
return this.GetType().GetHashCode();
}
}
WARNING: THIS CODE SUCKS, SEE ANTHONY'S COMMENTS
Which is faster?
1.
public bool IsValueType<T>(T obj){
return obj is ValueType;
}
2.
public bool IsValueType<T>(T obj){
return obj == null ? false : obj.GetType().IsValueType;
}
3.
public bool IsValueType<T>(T obj){
return default(T) != null;
}
4.Something else
You aren't really testing an object - you want to test the type. To call those, the caller must know the type, but... meh. Given a signature <T>(T obj) the only sane answer is:
public bool IsValueType<T>() {
return typeof(T).IsValueType;
}
or if we want to use an example object for type inference purposes:
public bool IsValueType<T>(T obj) {
return typeof(T).IsValueType;
}
this doesn't need boxing (GetType() is boxing), and doesn't have problems with Nullable<T>. A more interesting case is when you are passing object...
public bool IsValueType(object obj);
here, we already have massive problems with null, since that could be an empty Nullable<T> (a struct) or a class. But A reasonable attempt would be:
public bool IsValueType(object obj) {
return obj != null && obj.GetType().IsValueType;
}
but note that it is incorrect (and unfixable) for empty Nullable<T>s. Here it becomes pointless to worry about boxing as we are already boxed.
My first answer would be to write a simple test and find out for yourself.
My second answer (without any testing on my part, of course) would be option 1. It is the simplest check. The second method involves two separate checks while the third involves creating a default instance of a type.
You should also consider readability. The framework already gives you the ability to have the following in your code:
if(someObj is ValueType)
{
// Do some work
}
Why even bother creating a method that would simply turn the above statement into (assuming you made your method static and allowed the compiler to infer the generic type):
if(IsValueType(someObj))
{
// Do some work
}
Defining a struct actually defines two types: a value type, and a class type which derives from System.ValueType. If a request is made to create a variable, parameter, field, or array (collectively, 'storage location') of a type which derives from System.ValueType, the system will instead create a storage location which will store the object's fields rather than storing a reference to an object in which those fields appear. On the other hand, if a request is made to create an instance of a type deriving from System.ValueType, the system will create an object instance of a class which derives from System.ValueType.
This may be demonstrated by creating a struct which implements IValue:
interface IValue {int value {get; set;}};
struct ValueStruct : IValue
{
public int value {get; set;}};
}
with generic test routine and code to wrap it:
static void Test<T>(T it) where T:IValue
{
T duplicate = it;
it.value += 1;
duplicate.value += 10;
Console.WriteLine(it.value.ToString());
}
static void Test()
{
ValueStruct v1 = new ValueStruct();
v1.value = 9;
IValue v2 = v1;
Test<ValueStruct>(v1);
Test<ValueStruct>(v1);
Test<IValue>(v1);
Test<IValue>(v1);
Test<IValue>(v2);
Test<IValue>(v2);
}
Note that in every case, calling GetType on the parameter passed to Test would yield ValueStruct, which will report itself as a value type. Nonetheless, the passed-in item will only be a "real" value type on the first two calls. On the third and fourth calls, it will really be a class type, as demonstrated by the fact that a change to duplicate will affect it. And on the fifth and sixth calls, the change will be propagated back to v2, so the second call will "see" it.
static class Metadata<T>
{
static public readonly Type Type = typeof(T);
static public readonly bool IsValueType = Metadata<T>.Type.IsValueType;
}
//fast test if T is ValueType
if(Metadata<T>.IsValueType) //only read static readonly field!
{
//...
}
There are two rules:
1-All Classes are reference types such as Object and String, so it's supported by .NET Framework classes.
2-All structures are value types such as bool and char, even though it contain reference member, so it's supported by .NET Framework structures.
Simply right click on any type and Go To Definition if it's a Class so that means it a reference type else if it's a Struct so that means it's a value type :)
You can use
obj.GetType().IsValueType
This uses reflection but clear way instead of care of boxing unboxing.
Below is a sample of my dictionary definition. When I define DicKeys as a class, ContainsKey() doesn't work. If I change DicKeys to a struct, it works properly.
Why does ContainsKey() work differently for a class than it does for a struct?
Dictionary<DicKeys, DicVals> aDic = new Dictionary<DicKeys, DicVals>();
// original version as below
public Class DicKeys
{
public EnKey1 DicKeyItem1,
public EnKey2 DicKeyItem2,
}
// revised version as below
public struct DicKeys
{
public EnKey1 DicKeyItem1,
public EnKey2 DicKeyItem2,
}
// common parts of code below
public enum EnKey1
{
A1,
A2,
}
public enum EnKey2
{
B11,
B12,
}
DicKeys aDicKey = new DicKeys();
// assigned value to aDicKey obj here;
if (aDic.ContainsKey(aDicKey ) == true)
{
// do some thing here
// If I defined as 'class', it doesn't hit here.
// Updated to 'struct', it hit here.
}
Well, you haven't shown where you're populating the dictionary or what sfTKey is... but I strongly suspect it's because you haven't overridden Equals or GetHashCode in DicKeys.
By default, classes use reference identity for equality - in other words, two key references will only compare as equal if they refer to the exact same object. You can override Equals and GetHashCode to provide equality based on the contents of the objects though. Value types (structs) automatically use value-based equality, but I certainly wouldn't recommend using a mutable struct as you've got here. (I wouldn't recommend using public fields at all, to be honest.)
See this question for an example of the kind of thing you might do in Equals and GetHashCode - and some other members you may want to provide on types which have value-based equality.