How can I make my generic comparer (IComparer) handle nulls? - c#

I'm trying to write a generic object comparer for sorting, but I have noticed it does not handle the instance where one of the values it's comparing is null. When an object is null, I want it to treat it the same as the empty string. I've tried setting the null values to String.Empty but then I get an error of "Object must be of type String" when calling CompareTo() on it.
public int Compare(T x, T y)
{
PropertyInfo propertyInfo = typeof(T).GetProperty(sortExpression);
IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null);
IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null);
if (obj1 == null) obj1 = String.Empty; // This doesn't work!
if (obj2 == null) obj2 = String.Empty; // This doesn't work!
if (SortDirection == SortDirection.Ascending)
return obj1.CompareTo(obj2);
else
return obj2.CompareTo(obj1);
}
I'm pretty stuck with this now! Any help would be appreciated.

You cannot treat your T as an empty string unless your T was effectively constrained to being a string. What you should do is have a plan for comparing nulls. Such as
if (obj1 == null && obj2 == null)
return 0;
else if (obj1 == null)
return -1;
else if (obj2 == null)
return 1;
else
return obj1.CompareTo(obj2);

if (SortDirection == SortDirection.Ascending)
return Comparer<T>.Default.Compare(obj1, obj2);
else
return Comparer<T>.Default.Compare(obj2, obj1);

Since T is a generic type, you cannot assign it a String value; you can only assign it a value of type T. If you are only going to use this to compare strings, use String instead of T. Otherwise, add null checking and decide where in order null should fall.

IComparable obj1 = (IComparable)propertyInfo.GetValue(x, null) ?? "";
IComparable obj2 = (IComparable)propertyInfo.GetValue(y, null) ?? "";
This basically means that obj1 will now be the value of propertyInfo.GetValue(x, null) or, if that happens to be null, obj1 will be "".
Or if the problem is that the GetValue crashes on null you could do something like:
IComparable obj1 = "";
try { obj1 = (IComparable)propertyInfo.GetValue(x, null); } catch {}

Related

How to assign null values to dynamic property type

Basically, I am in need of a converter which converts a DataReader object to generic type.
so when I do -
while(dataReader.HasRows()){
var result= dataReader.ConvertToObject<MyModel>();
}
It calls an extension method of generic type -
public static T ConvertToObject<T>(this DbDataReader reader) where T : new()
{
T res = new T();
T t = new T();
for (int inc = 0; inc < reader.FieldCount; inc++)
{
Type type = t.GetType();
PropertyInfo prop = type.GetProperty(reader.GetName(inc));
var value = reader.GetValue(inc);
if (value == System.DBNull.Value)
{
value = null;
}
var targetType = IsNullableType(prop.PropertyType) ? Nullable.GetUnderlyingType(prop.PropertyType) : prop.PropertyType;
value = Convert.ChangeType(value, targetType);
prop.SetValue(t, value, null);
}
res = t;
return res;
}
private static bool IsNullableType(Type type)
{
return type.IsConstructedGenericType && type.GetGenericTypeDefinition().Equals(typeof(Nullable<>));
}
It works fine until the the result in datareader is System.DBNull except the System.String
To handle null values I forcefully checked if the value is type of System.DBNull then assign it a C# NULL object.
if (value == System.DBNull.Value)
{
value = null;
}
This however fails the conversion when the result is null for numeric type object.
So basically I am trying to do is when the result value is type - System.Nullable[System.Decimal]
then
if (value == System.DBNull.Value)
{
value = (System.Decimal?) null;
}
Or
value = (System.DateTime?)null //for DateTime
value = (System.Int32?)null //for Int32
What I tried to get dynamic property type
if (value == System.DBNull.Value)
{
value = (prop.PropertyType ?) null; // to get it dynamic
}
This didn't work. Please let me know what I have to do?
You can't specify null as a value of type T because it might not be a valid value for T. (Think int etc.)
However, you can use default(T), which will be the default value for T - the same value you'd get if you left a field of type T uninitialized, for example. That will be:
For reference types, a null literal
For nullable value types, the null value of the type
For non-nullable value types, the "all zero" value (0 for all numeric values etc)

? operator shows an "unreachable code detected" warning

I'm implementing an equals method and one of the equality conditions is that two dates match. My current code is:
public override bool Equals(object obj)
{
if (obj == null || obj.GetType() != typeof(Appointment))
return false;
Appointment other = obj as Appointment;
bool equal = true;
equal = (this.date == null ? (other.date == null) : (this.date.Equals(other.date)));
//more stuff here
return equal
}
when I try to build this, I get a warning pointing to the other.date==null statement saying unreachable code detected
however when I replace it with this:
if (this.date == null)
equal = other.date == null;
else
equal = (this.date.Equals(other.date));
I get no such warning
what am I missing?
I don't get such warning when the datatype is "object", but I get it as soon as I changed the datatype to "DateTime". So it has to do with the datatype. Datetime can't be null. Anyway you should check if "other" is null:
Appointment other = obj as Appointment;
bool equal = false; // init to false is better
if (other != null)
{
equal = (this.date == null ? (other.date == null) : (this.date.Equals(other.date)));
}
return equal;

A function to convert null to string

I want to create a function to convert any null value e.g. from a database to an empty string.
I know there are methods such as if value != null ?? value : String.Empty but is there a way to pass null to a method e.g.
public string nullToString(string? value)
{
if(value == null) return empty;
return value
}
But I am not sure on the parameter syntax to do this.
I tried the above but it says not a nullable type.
static string NullToString( object Value )
{
// Value.ToString() allows for Value being DBNull, but will also convert int, double, etc.
return Value == null ? "" : Value.ToString();
// If this is not what you want then this form may suit you better, handles 'Null' and DBNull otherwise tries a straight cast
// which will throw if Value isn't actually a string object.
//return Value == null || Value == DBNull.Value ? "" : (string)Value;
}
When you get a NULL value from a database, the value returned is DBNull.Value on which case, you can simply call .ToString() and it will return ""
Example:
reader["Column"].ToString()
Gets you "" if the value returned is DBNull.Value
If the scenario is not always a database, then I'd go for an Extension method:
public static class Extensions
{
public static string EmptyIfNull(this object value)
{
if (value == null)
return "";
return value.ToString();
}
}
Usage:
string someVar = null;
someVar.EmptyIfNull();
Convert.ToString(object) converts to string. If the object is null, Convert.ToString converts it to an empty string.
Calling .ToString() on an object with a null value throws a System.NullReferenceException.
EDIT:
Two exceptions to the rules:
1) ConvertToString(string) on a null string will always return null.
2) ToString(Nullable<T>) on a null value will return "" .
Code Sample:
// 1) Objects:
object obj = null;
//string valX1 = obj.ToString(); // throws System.NullReferenceException !!!
string val1 = Convert.ToString(obj);
Console.WriteLine(val1 == ""); // True
Console.WriteLine(val1 == null); // False
// 2) Strings
String str = null;
//string valX2 = str.ToString(); // throws System.NullReferenceException !!!
string val2 = Convert.ToString(str);
Console.WriteLine(val2 == ""); // False
Console.WriteLine(val2 == null); // True
// 3) Nullable types:
long? num = null;
string val3 = num.ToString(); // ok, no error
Console.WriteLine(num == null); // True
Console.WriteLine(val3 == ""); // True
Console.WriteLine(val3 == null); // False
val3 = Convert.ToString(num);
Console.WriteLine(num == null); // True
Console.WriteLine(val3 == ""); // True
Console.WriteLine(val3 == null); // False
You can just use the null coalesce operator.
string result = value ?? "";
Its possible to make this even shorter with C# 6:
public string NullToString(string value)
{
return value?.ToString() ?? string.empty;
}
Sometimes I just append an empty string to an object that might be null.
object x = null;
string y = (x + "").ToString();
This will never throw an exception and always return an empty string if null and doesn't require if then logic.
public string nullToString(string value)
{
return value == null ?string.Empty: value;
}
You can use Convert.ToString((object)value). You need to cast your value to an object first, otherwise the conversion will result in a null.
using System;
public class Program
{
public static void Main()
{
string format = " Convert.ToString({0,-20}) == null? {1,-5}, == empty? {2,-5}";
object nullObject = null;
string nullString = null;
string convertedString = Convert.ToString(nullObject);
Console.WriteLine(format, "nullObject", convertedString == null, convertedString == "");
convertedString = Convert.ToString(nullString);
Console.WriteLine(format, "nullString", convertedString == null, convertedString == "");
convertedString = Convert.ToString((object)nullString);
Console.WriteLine(format, "(object)nullString", convertedString == null, convertedString == "");
}
}
Gives:
Convert.ToString(nullObject ) == null? False, == empty? True
Convert.ToString(nullString ) == null? True , == empty? False
Convert.ToString((object)nullString ) == null? False, == empty? True
If you pass a System.DBNull.Value to Convert.ToString() it will be converted to an empty string too.
It is possible to use the "?." (null conditional member access) with the "??" (null-coalescing operator) like this:
public string EmptyIfNull(object value)
{
return value?.ToString() ?? string.Empty;
}
This method can also be written as an extension method for an object:
public static class ObjectExtensions
{
public static string EmptyIfNull(this object value)
{
return value?.ToString() ?? string.Empty;
}
}
And you can write same methods using "=>" (lambda operator):
public string EmptyIfNull(object value)
=> value?.ToString() ?? string.Empty;
you can use ??""
for example:
y=x??""
if x isn't null y=x but if x is null y=""
1. string.Format
You can use string.Format which converts null to empty string
string nullstr = null;
string quotestring = string.Format("{0}", nullstr);
Console.WriteLine(quotestring);//Output- ""
2.string interpolation
or you can use string interpolation. this feature is available in C# 6 and later versions.
InterpolatedExpression produces a result to be formatted. A string representation of the null result is String.Empty.
string nullstr = null;
string quotestring = $"{nullstr}";
Console.WriteLine(quotestring);//Output- ""
public string ToString(this string value)
{
if (value == null)
{
value = string.Empty;
}
else
{
return value.Trim();
}
}
Its an old topic, but there is a "elegant" way to do that...
static string NullToString( object Value )
{
return Value = Value ?? string.Empty;
}
You can try this
public string ToString(this object value)
{
// this will throw an exception if value is null
string val = Convert.ToString (value);
// it can be a space
If (string.IsNullOrEmpty(val.Trim())
return string.Empty:
}
// to avoid not all code paths return a value
return val;
}
you can use null-coalescing-operator C# 8.0 and later
value ??=string.Empty;
??= operators
Assign a nullable string to string.
string? input = "kushal";
string output = input?.ToString() ?? ""
According to the following MSDN page, you need the Convert.ToString method
string x = Convert.ToString((object)value)

Is it possible to modify the boolean value returned when checked if an instance of an object is null?

I was wondering if it is possible to modify the boolean value returned when checked if an instance of an object is null, for example (I know this is wrong and incomplete, just want to give you a reference):
Main:
SuperObject obj = new SuperObject();
if (obj == null) Console.WriteLine("It is null lol!");
SuperObject:
public bool destroyed = false;
public static bool operator ==(SuperObject A, object B)
{
if (A != null && B == null && destroyed == true)
return true;
}
So if the expression (A == null) is checked and A is NOT null but A.destroyed is TRUE, it will return that (A == null) is TRUE.
Basically I want (A == null) to be TRUE when:
A is really null OR A.destroyed = null; The default value for other comparisons.
I would recommend doing this instead:
public static bool IsDestroyed(SuperObject a) {
return (a == null || a.destroyed);
}
Your way would be very confusing to new developers.

Determine value of object in C#

What would be the best way to determine if an object equals number zero (0) or string.empty in C#?
EDIT: The object can equal any built-in System.Value type or reference type.
Source Code:
public void MyMethod(object input1, object input2)
{
bool result = false;
object compare = new object();
if(input != null && input2 != null)
{
if(input1 is IComparable && input2 is IComparable)
{
//do check for zero or string.empty
//if input1 equals to zero or string.empty
result = object.Equals(input2);
//if input1 not equals to zero or string.empty
result = object.Equals(input1) && object.Equals(input2); //yes not valid, but this is what I want to accomplish
}
}
}
Using Jonathan Holland code sample with a minor modification, here is the solution that worked:
static bool IsZeroOrEmpty(object o1)
{
bool Passed = false;
object ZeroValue = 0;
if(o1 != null)
{
if(o1.GetType().IsValueType)
{
Passed = (o1 as System.ValueType).Equals(Convert.ChangeType(ZeroValue, o1.GetType()))
}
else
{
if (o1.GetType() == typeof(String))
{
Passed = o1.Equals(String.Empty);
}
}
}
return Passed;
}
What's wrong with this?
public static bool IsZeroOrEmptyString(object obj)
{
if (obj == null)
return false;
else if (obj.Equals(0) || obj.Equals(""))
return true;
else
return false;
}
Michael, you need to provide a little bit more information here.
strings can be compared to null or string.Empty by using the method
string x = "Some String"
if( string.IsNullOrEmpty(string input) ) { ... }
int, decimals, doubles (and other numeric value-types) can be compared to 0 (zero) with a simple == test
int x = 0;
if(x == 0) { ... }
You can also have nullable value-types also by using the ? operator when you instantiate them. This allows you to set a value type as null.
int? x = null;
if( !x.HasValue ) { }
For any other object, a simple == null test will tell you if its null or not
object o = new object();
if( o != null ) { ... }
Hope that sheds some light on things.
Not quite sure the reasoning behind this, because .Equals is reference equality on reference types, and value equality on value types.
This seems to work, but I doubt its what you want:
static bool IsZeroOrEmpty(object o1)
{
if (o1 == null)
return false;
if (o1.GetType().IsValueType)
{
return (o1 as System.ValueType).Equals(0);
}
else
{
if (o1.GetType() == typeof(String))
{
return o1.Equals(String.Empty);
}
return o1.Equals(0);
}
}
Do you mean null or string.empty, if you're talking about strings?
if (String.IsNullOrEmpty(obj as string)) { ... do something }
Oisin
In the first case by testing if it is null. In the second case by testing if it is string.empty (you answered your own question).
I should add that an object can never be equal to 0. An object variable can have a null reference though (in reality that means the variable has the value of 0; there is no object in this case though)
obj => obj is int && (int)obj == 0 || obj is string && (string)obj == string.Empty

Categories

Resources