NibblyPig
NibblyPig

Reputation: 52932

Access properties by passing in a lambda

I am trying to write this method:

public static T Nullify<T>(T item, params Func<T, object> [] properties)
{
    // Sets any specified properties to null, returns the object.
}

I will call it like this:

var kitten = new Kitten() { Name = "Mr Fluffykins", FurColour = "Brown" };

var anonymousKitten = Nullify(kitten, c => c.Name);

However I am unsure of how to do this. Any ideas?

Upvotes: 0

Views: 1370

Answers (3)

Johan
Johan

Reputation: 8256

Another approach is to do this (it doesn't need to be an extension method)

public static T Nullify<T>(this T item, params Expression<Func<T, object>> [] properties)
{
    foreach(var property in properties)
    {
        var memberSelectorExpression = property.Body as MemberExpression;
        if (memberSelectorExpression != null)
        {
            var propertyInfo = memberSelectorExpression.Member as PropertyInfo;
            if (propertyInfo != null)
            {
                propertyInfo.SetValue(item, null, null);
            }
        }
    }

    return item;
}

Usage

item.Nullify(i => i.PropertyName, i => i.PropertyName2)

Upvotes: 5

ken2k
ken2k

Reputation: 48975

Without modifying your method definition much:

namespace ConsoleApplication
{
    public class Kitten : ISimpleClone<Kitten>
    {
        public string Name { get; set; }
        public string FurColour { get; set; }

        public int? Number { get; set; }

        public Kitten SimpleClone()
        {
            return new Kitten { Name = this.Name, FurColour = this.FurColour, Number = this.Number };
        }
    }

    public interface ISimpleClone<T>
    {
        T SimpleClone();
    }

    public class Program
    {
        public static PropertyInfo GetProperty<TObject, TProperty>(Expression<Func<TObject, TProperty>> propertyExpression)
        {
            MemberExpression body = propertyExpression.Body as MemberExpression;
            if (body == null)
            {
                var unaryExp = propertyExpression.Body as UnaryExpression;
                if (unaryExp != null)
                {
                    body = ((UnaryExpression)unaryExp).Operand as MemberExpression;
                }
            }

            return body.Member as PropertyInfo;
        }

        public static T Nullify<T>(T item, params Expression<Func<T, object>>[] properties)
            where T : ISimpleClone<T>
        {
            // Creates a new instance
            var newInstance = item.SimpleClone();

            // Gets the properties that will be null
            var propToNull = properties.Select(z => GetProperty<T, object>(z));
            var filteredProp = propToNull
                .Where(z => !z.PropertyType.IsValueType || Nullable.GetUnderlyingType(z.PropertyType) != null) // Can be null
                .Where(z => z.GetSetMethod(false) != null && z.CanWrite); // Can be set
            foreach (var prop in filteredProp)
            {
                prop.SetValue(newInstance, null);
            }

            return newInstance;
        }

        public static void Main(string[] args)
        {
            var kitten = new Kitten() { Name = "Mr Fluffykins", FurColour = "Brown", Number = 12 };

            var anonymousKitten = Nullify(kitten, c => c.Name, c => c.Number);

            Console.Read();
        }
    }
}

Looks a bit hacky though....

Upvotes: 0

Matyas
Matyas

Reputation: 1172

You'd need to pass a "setter method" not a "reader method" in properties.

static void Nullify<T, D>(T item, params Action<T, D>[] properties)
    where D : class
{
    foreach (var property in properties)
    {
        property(item, null);
    }
}

usage:

Nullify<Kitten, string>(kitten, (c, d) => { c.Name = d; });

But this will just set the data for you. If you want a copy and then apply the properties, the items would probably have to be clonable (alternatively you can go though some hell with reflection):

static T Nullify<T, D>(T item, params Action<T, D>[] properties)
    where D : class
    where T : ICloneable
{
    T copy = (T)item.Clone();

    foreach (var property in properties)
    {
        property(copy, null);
    }

    return copy;
}

class Kitten : ICloneable
{
    public string Name { get; set; }
    public string FurColour { get; set; }

    public object Clone()
    {
        return new Kitten() { Name = this.Name, FurColour = this.FurColour };
    }
}

usage

var anonymousKitten = Nullify(kitten, (c, d) => { c.Name = d; });

Upvotes: 2

Related Questions