On-the-fly fields from Static Reflection?

881 views Asked by At

I've been studying up on static reflection with LINQ expressions - very cool!

One thought I had - is it possible for one class to 'generate' fields on one class based on static reflection done on another class? I'm thinking specifically of the Builder pattern I've seen here many times. I would like to do a fluent-nhibernate-style property registration that 'generates' fields on the builder that match the class I want to build. Soemthing like this:

public class Color
{
    private Color()
    {
    }
    public string Name { get; private set; }

    public class Builder : BuilderBase<Color>
    {
        public Builder()
        {
            Property(x => x.Name);
        }
        public Build()
        {
            return built_up_color;
        }
    }
}

and support constructor syntax like this:

Color c = new Color.Builder() { Name = "Red" }.Build();

The point of all this is to reduce the number of times I have to repeat defining the properies of Color. I played with this:

public class Color
{
    private Color()
    {
    }
    public string Name { get; private set; }

    public class Builder
    {
        private Color _color = new Color();
        public string Name
        {
            get { return _color.Name; }
            set { _color.Name = value; }
        }

        public Build()
        {
            return _color;
        }
    }
}

which certainly works AND lists the properties the same # of times but feels wordier and less flexible. It seems like I should be able to do something anonymous-type-y here?

3

There are 3 answers

2
ShuggyCoUk On BEST ANSWER

Worth pointing out that Having a class called Color clashing with the System.Drawing.Color is probably a bad idea.

It is very likely to lead to confusion in others (worse still System.Drawring.Color has value semantics whereas your class has reference semantics which can lead to further confusion)

I would point out that what you really want is Named Optional Arguments. I would suggest that putting in cumbersome Builder classes now will be more effort and made it more painful to move to these once you get to c# 4.0. Instead make the constructors that are required (or if need be to avoid type signature collisions static factory methods on the class)

2
Anton Tykhyy On

I think it's impossible, you can't generate members except by declaring them explicitly. Bite the bullet and declare a constructor for Color.

PS: I think static reflection is a misnomer, the only thing that's static is the lookup of the member you want to reference — a good thing as far as it goes, but that's not very far.

0
Firo On

less code to write but using reflection to set the values.

The trick is to use collection initializers. it is typesafe.

public class Color
{
    private Color()
    {
    }
    public string Name { get; private set; }
    public int Prop2 { get; private set; }

    public class Builder : Builder<Color>
    {
        public Builder()
        {
            // possible
            _instance.Name = "SomeDefaultValue";
        }
    }
}

class Builder<T> : IEnumerable<string>
{
    protected T _instance = Activator.CreateInstance(typeof(T));

    public void Add<TProperty>(Expression<Func<T, TProperty>> prop, TProperty value)
    {
        StaticReflection.GetPropertyInfo(prop).SetValue(_instance, value, null);
    }

    public static implicit operator T(Builder<T> builder)
    {
        return builder.Build();
    }

    public T Build()
    {
        return _instance;
    }

    IEnumerator<string> IEnumerable<string>.GetEnumerator()
    {
        // e.g. return iterator over the property names
        throw new NotImplementedException();
    }

    IEnumerator IEnumerable.GetEnumerator()
    {
        return ((IEnumerable<string>)this).GetEnumerator();
    }
}

and call syntax

var color = new Color.Builder
{
    { x => x.Name, "foo" },
    { x => x.Prop2, 5 }
}.Build();

// or

var color = new Builder<Color>
{
    { x => x.Name, "foo" },
    { x => x.Prop2, 5 }
}.Build();

// or

Color color = new Builder<Color>
{
    { x => x.Name, "foo" },
    { x => x.Prop2, 5 }
};