11
\$\begingroup\$

I have created a method to check for null/empty values of class properties and if any null property is found I'm stopping the checking process and returning the result as true. I've used a solution from here and following is the code I have implemented:

/// To check the properties of a class for Null/Empty values
/// </summary>
/// <param name="obj">The instance of the class</param>
/// <returns>Result of the evaluation</returns>
public static bool IsAnyNullOrEmpty(object obj)
{
    //Step 1: Set the result variable to false;
    bool result = false;

    try
    {
        //Step 2: Check if the incoming object has values or not.
        if (obj != null)
        {
            //Step 3: Iterate over the properties and check for null values based on the type.
            foreach (PropertyInfo pi in obj.GetType().GetProperties())
            {
                //Step 4: The null check condition only works if the value of the result is false, whenever the result gets true, the value is returned from the method.
                if (result == false)
                {
                    //Step 5: Different conditions to satisfy different types
                    dynamic value;
                    if (pi.PropertyType == typeof(string))
                    {
                        value = (string)pi.GetValue(obj);
                        result = (string.IsNullOrEmpty(value) ? true : false || string.IsNullOrWhiteSpace(value) ? true : false);
                    }
                    else if (pi.PropertyType == typeof(int))
                    {
                        value = (int)pi.GetValue(obj);
                        result = (value <= 0 ? true : false || value == null ? true : false);
                    }
                    else if (pi.PropertyType == typeof(bool))
                    {
                        value = pi.GetValue(obj);
                        result = (value == null ? true : false);
                    }
                    else if (pi.PropertyType == typeof(Guid))
                    {
                        value = pi.GetValue(obj);
                        result = (value == Guid.Empty ? true : false || value == null ? true : false);
                    }
                }
                //Step 6 - If the result becomes true, the value is returned from the method.
                else
                    return result;
            }
        }
    }
    catch (Exception ex)
    {
        throw ex;
    }

    //Step 7: If the value doesn't become true at the end of foreach loop, the value is returned.
    return result;
}

I want to extend this code to another end that instead of sending the bool result as true or false, I'll clone the incoming object and based on the values inside the properties can send the object.

For example:

Let's say this is my model: Order.cs

public class Order
{

    [DataMember]
    public Guid OrderId { get; set; }

    [DataMember]
    public string Owner { get; set; }

    [DataMember]
    public string Info { get; set; }

    [DataMember]
    public string Recipient { get; set; }

    [DataMember]
    public int Test { get; set; }

    [DataMember]
    public DateTime CreatedOn { get; set; }
}

Now if there is any null or empty value in any of the properties, I'll be returning a new class which contains only those values which are either null and empty and the status of it as:

//This class is the output class from the IsAnyNullOrEmpty() method and has only those properties which are either null or empty
public class ProcessedModel
{       
    public string Property1 { get; set; }   //has null value in type String (set property vaule to "Null value")

    public string Property2 { get; set; } //has empty value in type String (set property vaule to "Empty value")

    public string Property3 { get; set; } //has 0 value in type int (set property vaule to "0 value")
}

Am I following the right approach?

\$\endgroup\$
  • \$\begingroup\$ Please specify an Empty value. If an int is 0, this is probably a default value. However, it might be a nominal meaningful value that happens to be 0. If an Nullable<int> is 0, it is assigned, but is it an Empty value? How would you detect the Empty value of an IDictionary<IEnumerable,Tuple<int?,IList>>? \$\endgroup\$ – dfhwze May 18 at 20:33
  • \$\begingroup\$ Indeed, this becomes even more problematic when we deal with PODs (Plain Old Data types) such as strings, where null and Empty might be treated differently, vs. custom user types where Empty might not be easily defined or be meaningful as opposed to null. Also, reference types vs. value types have their own differences when it comes to what Empty even means, as they cannot by definition be null. \$\endgroup\$ – Stacy Dudovitz May 20 at 16:48
  • \$\begingroup\$ @StacyDudovitz This is the reason why I favor validation attributes over a generic approach. The specification is not strict enough to accomodate a generic solution using reflection. (which is a pitty) \$\endgroup\$ – dfhwze May 27 at 8:13
17
\$\begingroup\$

First of all I'd reduce indentation. It makes your code really too hard to read. Doing that you will see it may be simplified.

First of all try/catch: a catch-all block where you rethrow the exception is useless (and you may even throw away line number information.)

This code:

if (obj != null)
{
    // Do something...
}

May be replaced simply with:

if (obj == null)
    return false;

You do not need to declare a local variable for result, simply return its value where appropriate.

You do not have to check condition result == false inside your foreach loop (you will still enumerate through all properties), simply break the loop and exit immediately.

You do not need to use to dynamic, when casting you have the right type at compile-time. Also move all this code inside a separate function:

private static bool IsNullOrEmpty(object obj) {
}

Let's see how to fill this function. Let's read the value of the property:

object value = pi.GetValue(obj);

First of all a common case, valid for all reference types:

if (Object.ReferenceEquals(value, null))
    return true;

Then explicitly check for strings:

if (value is string && String.IsNullOrEmpty((string)value))
    return true;

For Int32 (note that in your original code value <= 0 ? true : false is redundant it may be simplified to value <= 0). BTW are you sure you want to consider negative numbers as empty values?!

if (value is int)
    return ((int)value) <= 0;

Boolean does not need to be checked in this case (null case has been already handled). We just need to work out Guid:

if (value is Guid)
    return ((Guid)value) == Guid.Empty;

That's all. Note that now or in future you may want to check for Nullable<T> and or other types. With the exception of strings and negative integer numbers you can also do something like this:

if (Object.ReferenceEquals(value, null))
    return true;

var type = value.GetType();
return type.IsValueType
    && Object.Equals(value, Activator.CreateInstance(type));

What left in your original function? In this example let me use simplified check (negative numbers are not empty and I do not check for String.Empty against strings however it handles nullable types), feel free to pick the one works for you:

public static bool IsAnyNullOrEmpty(object obj)
{
    if (Object.ReferenceEquals(obj, null))
        return true;

    return obj.GetType().GetProperties()
        .Any(x => IsNullOrEmpty(x.GetValue(obj)));
}

private static bool IsNullOrEmpty(object value)
{
    if (Object.ReferenceEquals(value, null))
        return true;

    var type = value.GetType();
    return type.IsValueType
        && Object.Equals(value, Activator.CreateInstance(type));
}

Last note: if working with databases you may also want to check equality with DBNull.Value.

\$\endgroup\$
  • \$\begingroup\$ -Thanks for the prompt reply and detailed feedback, you have reduced a significant amount of code from the existing approach. I have one doubt regarding try-catch. You have suggested to drop the try-catch but i'm using it to track any errors or may be for logging in future. As per the best practices are considered, the value of the function should be returned at the end that's the only reason for declaring result variable. Also I have one more requirement as asked in the question to return a new class with only null values Can you share your thoughts on it? Many thanks for the answer \$\endgroup\$ – iSahilSharma Nov 23 '16 at 9:45
  • \$\begingroup\$ try/catch: if you have an exception handling policy (you should!) you probably don't want to handle any exception here. Let me explain: reading a property should never ever throw an exception. If it does then you forgot something you should handle/do/redesign. In that case you want to fix it during development, no need to hide this error. \$\endgroup\$ – Adriano Repetti Nov 23 '16 at 9:50
  • 2
    \$\begingroup\$ 2nd, at function level, you should catch only exceptions you know may happen. Is an OutOfMemoryException reasonable here? Has it anything to do with reading properties? What about AccessViolationException? Catching Exception you're hiding possibly edge cases you MUST handle in your code (which if just goes ignored and swallowed are possible latent bugs and/or performance hits you do not want) \$\endgroup\$ – Adriano Repetti Nov 23 '16 at 9:50
  • 2
    \$\begingroup\$ Single exit point is an abandoned best practice. It had some sense (and it may have) in other languages but it's not something you want to follow in C#. There are still cases where it may be desiderable (for example extreme mission critical software) but not in 99.9999% of software (and probably not in something we will ever write in our life). Easier to read code and shorter+simpler functions are more often than not more advantageous than its drawbacks (slightly harder to follow execution path, which BTW should not be an issue if you keep your functions very short) \$\endgroup\$ – Adriano Repetti Nov 23 '16 at 9:56
  • 1
    \$\begingroup\$ Did you mean to return 'true' when Object.ReferenceEquals(obj, null) ? \$\endgroup\$ – dfhwze May 18 at 20:29
4
\$\begingroup\$

This is an interesting approach but I would prefer to do this via attributes, for example lets take the following sample class:

public class SampleClass 
{
    public int Property1 { get; set; }
    public bool Property2 { get; set; }
    public Guid Property3 { get; set; }
    public string Property4 { get; set; }
    public object Property5 { get; set; }
}

Now let's suppose we want to validate this class, but we need to support quite a few types. We don't want to have to modify the validation logic each time we add a new type, so lets create a validation attribute:

[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public abstract class ValidatorAttribute : Attribute
{
    public abstract bool Validate(object value);
}

Now we can make specific validation attributes:

public class DefaultValidatorAttribute : ValidatorAttribute
{
    public override bool Validate(object value)
    {
        if (value is bool)
            return (bool)value != false;

        if (value is string)
            return !string.IsNullOrWhiteSpace((string)value);

        if (value.GetType().IsValueType && value.GetType().IsPrimitive)
            return (double)value != 0.0;

        return value != null;
    }
}

public class IntValidatorAttribute : ValidatorAttribute
{
    public override bool Validate(object value)
    {
        return (int)value <= 0;
    }
}

public class GuidValidatorAttribute : ValidatorAttribute
{
    public override bool Validate(object value)
    {
        if (!(value is Guid))
            return false;
        return ((Guid)value) != Guid.Empty;
    }
}

And decorate our class:

public class SampleClass
{
    [IntValidator]
    public int Property1 { get; set; }
    public bool Property2 { get; set; }
    [GuidValidator]
    public Guid Property3 { get; set; }
    public string Property4 { get; set; }
    public object Property5 { get; set; }
} 

Now all we need is the validation logic. I think it is acceptable to make an extension method for this:

public static class ValidationExtensions
{
    public static bool AllPropertiesValid(this object obj)
    {
        if (obj == null)
            return false;

        return obj.GetType().GetProperties().All(p =>
        {
            var attrib = p.GetCustomAttributes(typeof(ValidatorAttribute), true)
                                         .FirstOrDefault() as ValidatorAttribute;

            if (attrib == null)
                attrib = new DefaultValidatorAttribute();

            return attrib.Validate(p.GetValue(obj));
        });
    }
}

So, as you can see, this separates concerns, and we can do fun things like this:

public class SkipValidator : ValidatorAttribute
{
    public override bool Validate(object value)
    {
        return true;
    }
}

Which allows us to easily extend without changing the validation logic, now we can skip properties:

public class SampleClass
{
    [SkipValidator]
    public int DoNotCare { get; set; }
}

Or if you need special string validators, etc. You can do this without breaking your existing code, invalidating testing, and without introducing a new source of bugs in working code. Additionally, unit testing is a lot cleaner since you can test individual cases against specific attributes.

\$\endgroup\$
  • \$\begingroup\$ I wonder why you got downvoted. I find it's a great solution that I also was going to suggest. It should however be mentioned that it works only with own types not with 3rd-party. \$\endgroup\$ – t3chb0t Nov 24 '16 at 7:46
  • 1
    \$\begingroup\$ Validation rules are a broader concept than null/empty checks. They may be an overkill (if you just need to check for nulls) or an invaluable tool, what I do not understand is why reinventing the wheel when we already have data annotations? \$\endgroup\$ – Adriano Repetti Nov 24 '16 at 10:19
  • \$\begingroup\$ @Adriano Repetti the reason I prefer validation rules over null/empty checks is there is no uniform definition for 'empty', so making seperate validators for each specific case is the way to go. You could define both a NotZeroIntValidator and a StrictPositiveIntValidator (what the OP wants) and decide when to use which. If it would have been just NULL checks, I would decide against validation rules. \$\endgroup\$ – dfhwze May 20 at 15:07
  • \$\begingroup\$ @dfhwze I'm not against validators (on the contrary...) if the task is generic enough and you have access to the source code of the classes you want to decorate. I was dubious here about the use of custom annotations when we already have Data Annotations. I didn't even reviewed that part of the OP's question because...well...there isn't any actual code to review. \$\endgroup\$ – Adriano Repetti May 20 at 15:32
4
\$\begingroup\$

There are several problems with the answers here, most notably that the original question doesn't seem to be answered, namely, that we want to create a clone a class with ONLY the fields that are set to default/NULL. But lets put a pin in that for a moment.

Lets break this problem down into three parts:

  1. We need a way to specify which properties we want to validate (and we should be able to overlap that with multiple business requirements)
  2. We need a way to generically walk the class recursively to find all fields that are either NULL or set to their default value for all types.
  3. We need to construct some class as a return type that reflects all of those fields that are NULL/default.

Fields to be Validated

The first requirement is fairly straight forward, as .NET has been offering this for quite some time. We can use property decorators, which will allow use to use reflection to walk the properties when we move on to step (2). We achieve this using the following code (for this example, I am choosing to use the decorator name 'Validate', but you can really use anything you like):

[AttributeUsage(AttributeTargets.Property)]
public class ValidateAttribute : Attribute
{
    /// <summary>
    /// Name
    /// </summary>
    public string Validate
    { get; set; }
}

The beauty of decorators and reflection is that they are both self commenting, and can be mixed and matched. Supposed you want to validate one set of properties for new records, but another set only for update. You could create two decorators as follows:

[AttributeUsage(AttributeTargets.Property)]
public class NewValidateAttribute : Attribute
{
    /// <summary>
    /// Name
    /// </summary>
    public string Validate
    { get; set; }
}

[AttributeUsage(AttributeTargets.Property)]
public class UpdateValidateAttribute : Attribute
{
    /// <summary>
    /// Name
    /// </summary>
    public string Validate
    { get; set; }
}

Your class could then be implemented as follows:

public class Foo
{
    [NewValidate]
    public string Name { get; set; }

    [NewValidate]
    [UpdateValidate]
    public string Age { get; set; }

    [UpdateValidate]
    public bool LikesIceCream { get; set; }
}

Validation

The next task is the actual validation. Here is where things should be done in a way that should support the following idioms:

  • Should be able to handle POD (plain old data) types (both value and reference types)
  • Should be able to handle user types
  • Should be able to handle Generic (e.g. ICollection, etc.)
  • Should be able to handle nested classes to n-levels
  • Should be able to handle any decorator type 'T'
  • Should follow separation of concerns pattern to support unit testing

If we separate out these requirements, we can conclude that this really boils down to:

  • Walk the target object looking for all public properties with a decorator of type 'T'
  • Find the value of that property and decide if we need to recurs (go back to step 1)
  • If its a reference type, do the check, make a note and move on
  • If its a value type, create an instance to get its default, compare, do the check, make a note, and move on
  • If its a generic, same this, etc.

Let's first walk the object:

    /// <summary>
    /// Iterates through all properties in the object 'obj' with the attribute 'T' and compiles a list of property
    /// names whose value is the default value (i.e. the value assigned when the property is fixed created).
    /// </summary>
    /// <param name="obj">The object whose properties we will iterate</param>
    /// <returns>a list of property names as strings</returns>
    /// <remarks>
    /// This method works on the assumption that default values for properties are never valid values. For example, an
    /// integer property cannot contain '0' as a valid value.
    /// </remarks>
    public static List<string> FindAllPropertiesWithDefaultValues<T>(object obj)
    {
        // collection of properties that contain default values
        var defaultProps = new List<string>();

        // Find all properties (using reflection) that contain an attribute of type 'type'
        var props = obj.GetType().GetProperties().Where(prop => Attribute.IsDefined(prop, typeof(T)));
        props.ToList().ForEach(prop =>
        {
            // get the value and default value of 'prop'
            var val = prop.GetValue(obj);
            var defVal = Factory.GetDefaultValueForType(prop.PropertyType);

            // If the property contains a default value (such as what happens when an object is first created without setting any
            // values), is null or is an empty generic collection, add it to the defaultProps collection.
            // In the case of generic values, this test will fail which is not an issue since we cant test generic properties anyways.
            if (val == null || val.Equals(defVal) || IsEmptyGenericCollection(prop, obj))
            {
                var thisType = prop.DeclaringType?.Name ?? "<Unknown>";
                defaultProps.Add($"{thisType}:{prop.Name}");
            }

            // Don't step into user type if the type itself is null
            if (prop.GetValue(obj) != null)
            {
                // Make recursive call to this function
                defaultProps.AddRange(FindAllPropertiesWithDefaultValues<T>(prop.GetValue(obj)));
            }
        });

        // Check if all of the validators returned success and return the result of that check
        return defaultProps;
    }

This method returns a collection of 'classname':'property' strings for all properties that are found to be either NULL or default.

Lets talk about two methods here for a moment. The IsEmptyGenericCollection method checks to see if the object has the IsGenericType property set, or if the object is a descendant of IEnumerable. In any case, this is where the special tests are then made to determine if the collection object is empty, since the presence of a fully constructed container is not enough evidence of a 'default' object -- we need to know the presence/absence of elements to make that determination.

    /// <summary>
    /// Test to see if the property of the object 'obj' is a generic collection, and if so, test to see if that collection
    /// has any elements.Is
    /// </summary>
    /// <param name="prop">The property of 'obj' that we are testing</param>
    /// <param name="obj">The object that owns the property 'prop'</param>
    /// <returns>true is the property is a generic collection with no elements, false otherwise</returns>
    private static bool IsEmptyGenericCollection(PropertyInfo prop, object obj)
    {
        // sanity
        if (prop == null)
        {
            throw new ArgumentNullException(nameof(prop));
        }

        // Check to see if this is a generic collection. All generic collections are derived from IEnumerable as well
        // as IEnumerable<T>.
        if (!prop.PropertyType.IsGenericType || !(prop.GetValue(obj) is System.Collections.IEnumerable))
        {
            return false;
        }

        // retrieve the enumerator and check to see if there is an element to advance to... if not, then we
        // are in the default state
        var enumerable = prop.GetValue(obj) as System.Collections.IEnumerable;
        if (enumerable == null)
        {
            return false;
        }
        var enumerator = enumerable.GetEnumerator();
        var isEmptyGenericCollection = !enumerator.MoveNext();

        return isEmptyGenericCollection;
    }

The second method needed is the instance creator for value types. I chose to move that off to a separate class because thats how I partitioned things for testing in my projects.

public static class Factory
{
    /// <summary>
    /// Create a default object of a given type
    /// </summary>
    /// <param name="type">The type to create</param>
    /// <returns>The type in its default state</returns>
    public static object GetDefaultValueForType(Type type)
    {
        return type.IsValueType ? Activator.CreateInstance(type) : null;
    }
}

Constructing a Class

Before we tackle this last point, and to my mind was what the questioner was asking in the first place, lets create a test case.

Lets first make some test classes as follows:

public class ExampleClassB
{
    [Validate]
    public double SomeDouble { get; set; }

    [Validate]
    public Rectangle SomeRectangle { get; set; }
}


public class ExampleClassA
{
    /// <summary>
    /// Ctor
    /// </summary>
    public ExampleClassA()
    {
        someList = new List<string>();
        SomeClassB_1 = new ExampleClassB();
    }

    [Validate]
    public string SomeString { get; set; }

    [Validate]
    public int SomeInt { get; set; }

    [Validate]
    public TimeSpan SomeTimeSpan { get; set; }

    [Validate]
    public Guid SomeGuid { get; set; }

    private List<string> someList;
    [Validate]
    public List<string> SomeList => someList;

    [Validate]
    public ExampleClassB SomeClassB_1 { get; set; }

    [Validate]
    public ExampleClassB SomeClassB_2 { get; set; }

    public string WontBeValidated { get; set; }
}

This test will test the following:

  • POD types for both reference types and value types (I chose Guid, TimeSpan, DateTime as well as some classics like int and double as well as Rectangle for you graphics types out there)
  • IList for testing generics
  • Recursive testing of nested classes, one that is instantiated, one that remains dangling (NULL)

We test this as follows in a console app:

/// <summary>
/// Main program
/// </summary>
internal static class Program
{
    /// <summary>
    /// Main entry point
    /// </summary>
    /// <param name="args"></param>
    static void Main(string[] args)
    {
        var exampleClass = new ExampleClassA();
        var results = Validate.FindAllPropertiesWithDefaultValues<ValidateAttribute>(exampleClass);
        results.ForEach(Console.WriteLine);
        Console.ReadLine();
    }
}

Our output looks as follows:

enter image description here

So this begs the question... why would you want to return a cloned class with only the properties present that are NULL default? Is that really your intent?

Some possibilities...

A collection (List? Set/Hash? Dictionary? etc) of classes/properties that are NULL/default? It could look something like this:

public class DefaultProp
{
    public string Class { get; set; }
    public string Property { get; set; }
    public string Type { get; set; }
}

var PropList = new List<DefaultProp>();

You could then do whatever you need to with this information.

Just my two shekels...

\$\endgroup\$
  • \$\begingroup\$ The problem I have with the OP's question, is (1) a proper definition for Empty and (2) what is the actual usage scenario for this problem? Your answer might be valid, but perhaps a new List<List<int>>{ new List<int> { 0 } } is also considered Empty, and so on... \$\endgroup\$ – dfhwze May 20 at 15:03
  • \$\begingroup\$ Agreed on point (1). On point (2), one could imagine adding in a custom Func<t, ...> as a last parameter (as optional = null perhaps?), which would allow a custom isEmpty lambda to be defined by the caller to handle such cases if such complex data structures were thought to be encountered. \$\endgroup\$ – Stacy Dudovitz May 20 at 16:53
  • 1
    \$\begingroup\$ That's why the other answers didn't address that part of the question. Not only we do not have enough (if any) context but we do not even have any code to review (and this is CR, not SO). \$\endgroup\$ – Adriano Repetti May 24 at 17:30
  • \$\begingroup\$ @AdrianoRepetti I agree. This question lacks crucial information. \$\endgroup\$ – dfhwze May 27 at 8:15
3
\$\begingroup\$

@Adriano Repetti was faster with his list of issues ;-) so I'll just post an alternative soltuion where you can have everyting in only one linq query using the All extension. It will stop as soon as some property doesn't meet the conditions.

Consider changing the name to AllPropertiesValid because with your custom conditions like value <= 0 there is no default solution for all properties and it better shows that it checks the properties of the parameter.

public static bool AllPropertiesValid(object obj)
{
    return !obj.GetType().GetProperties().All(p =>
    {
        var value = p.GetValue(obj);

        if (value == null) { return false; }

        if (p.PropertyType == typeof(string))
        {
            return string.IsNullOrEmpty((string)value);
        }

        if (p.PropertyType == typeof(int))
        {
            return ((int)value <= 0);
        }

        if (p.PropertyType == typeof(bool))
        {
            return (!(bool)value);
        }

        if (p.PropertyType == typeof(Guid))
        {
            return ((Guid)value) == Guid.Empty;
        }

        return true;
    });
}

(I hope I got the conditions right)

\$\endgroup\$
  • \$\begingroup\$ else if as the OP had it originally would be more efficient \$\endgroup\$ – paparazzo Nov 23 '16 at 15:51
  • \$\begingroup\$ @Paparazzi any proof? \$\endgroup\$ – t3chb0t Nov 23 '16 at 15:56
  • \$\begingroup\$ Why would you test for int if you know it is string? Sorry I tried to help. \$\endgroup\$ – paparazzo Nov 23 '16 at 16:13
  • \$\begingroup\$ @Paparazzi I believe you oversaw the return false in the same line with the if - it stops at the first match. \$\endgroup\$ – t3chb0t Nov 23 '16 at 16:20
  • 1
    \$\begingroup\$ @Paparazzi crap, now I see what you mean... \$\endgroup\$ – t3chb0t Nov 23 '16 at 16:23
2
\$\begingroup\$

If you decide to go the generic way, without applying attributes on your properties, we could build further on Adriano Repetti's answer. My starting point is the IsNullOrEmpty method, which is invoked against any property in the tree of properties of any specified complex object or list of properties of any specified simple object.

The goal is to test against default values and common empty values.

private static bool IsNullOrEmpty(object value)
{
    if (Object.ReferenceEquals(value, null))
        return true;

    var type = value.GetType();
    return type.IsValueType
        && Object.Equals(value, Activator.CreateInstance(type));
}

By performing not just Object.ReferenceEquals but also ==, we can test both against references as overloaded operators. Nullable<> has an overload that matches == null when HasValue is False. Furthermore, we can check value against the default instance of its type by calling ObjectFactory.CreateInstance. Note that string and DBNull have custom empty values that do not correspond to Activator.CreateInstance.

private static bool IsNullOrEmpty(object value)
    {
        if (Object.ReferenceEquals(value, null) || value == null)
            return true;

        // common Empty values
        if (value is string && string.IsNullOrEmpty((string)value)) {
            return true;
        }
        if (value is DBNull && (DBNull)value == DBNull.Value) {
            return true;
        }

        var type = value.GetType();
        var typeDefault = ObjectFactory.CreateInstance(type);

        if (Object.ReferenceEquals(typeDefault, null) || typeDefault == null)
            return false;
        if (Object.ReferenceEquals(value, typeDefault) || value == typeDefault)
            return true;

        return false;
    }

Appendix: ObjectFactory

ObjectFactory creates the default value of any type. Do not confuse this with the empty value. The latter is context-bound and should be specified on a case-by-case problem. By default, only string.Empty and DBNull.Value are considered empty.

public static class ObjectFactory
    {
        public static object CreateInstance(Type type) 
        {
            if (TypeSystem.IsNullAssignable(type.NotNull())) {
                return null;
            }
            if (type.IsEnum) {
                return Enum.ToObject(type, Convert.ChangeType(0, 
                     Enum.GetUnderlyingType(type)));
            }
            if (type.IsValueType) {
                return Activator.CreateInstance(type);
            }
            return null;
        }
    }

Appendix: TypeSystem

TypeSystem consists of convenience methods for verifying characteristics of a Type. Only the relevant methods are included for brevity.

public static class TypeSystem
    {
        public static bool IsNullable(Type type) 
        {
            return type.NotNull().IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>);
        }

        public static bool IsNullAssignable(Type type) 
        {
            if (IsNullable(type.NotNull()) || !type.IsValueType) {
                return true;
            }

            if (!type.IsGenericParameter) {
                return false;
            }

            #region Generic Parameter Handling
            // probably out-of-scope for this problem
            var gpa = type.GenericParameterAttributes & GenericParameterAttributes.SpecialConstraintMask;

            if (GenericParameterAttributes.None != (gpa & GenericParameterAttributes.ReferenceTypeConstraint)) {
                return true;
            }

            if (GenericParameterAttributes.None != (gpa & GenericParameterAttributes.NotNullableValueTypeConstraint)) {
                return false;
            }

            var gpc = type.GetGenericParameterConstraints();

            if (gpc == null || !gpc.Any(x => x.IsClass && IsNullAssignable(x))) {
                return false;
            }
            #endregion

            return true;
        }
    }
\$\endgroup\$
0
\$\begingroup\$

If you sometimes want a bool and sometime you want a list of properties and values I would suggest changing so you return an IEnumerable of type ValidationResult

IF you change your method to use yield returns when you want a bool you can just use the Linq Any() method and it will stop after finding the first one. Using IEnumerable you are not limited to 3 properties like in your class you built.

I agree with everything Adriano Repetti posted and instead of returning false you could just yield return a ValidationResult class with the information you need.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.