Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

> You are thinking that using reflection and casting at runtime is not a sensible thing because it is not optimal

No I'm not thinking that, I'm saying that implementing a non-generic base interface like

    public interface IValidator {
        bool Validate(object obj);
    }
is pointless because you'll have to do the same type checking/casting in Validate you would have to do in a wrapper which implemented IValidator<object>. Requiring a base interface is worse because you'll have to implement the same boilerplate implementation of Validate(object) in every implementing class - see IEnumerable/IEnumerable<T> as an example.

> Obviously this library will need to do run-time casting of types

You've linked to a clojure library which is dynamically typed so I don't see how this is relevant. It's not obvious to me why a C# version would require casting.

Task/Task<T> does follow the pattern but they could have simply introduced a Unit type in the BCL and replaced the non-generic Task type with Task<Unit> instead.

> To run all of these validators against a given class you have three (un)reasonable choices:

You only need to write one wrapper class which encapsulates the cast you'd have to do anyway in a non-generic IValidator interface:

    public class Wrapper<T, TBase> : IValidator<TBase>
    {
        private readonly IValidator<T> inner;
        public Wrapper(IValidator<T> v)
        {
            this.inner = v;
        }
        public bool Validate(TBase b)
        {
            if(b is T)
            {
                return this.inner.Validate((T)(object)b);
            }
            else return false;
        }
    }


> Task/Task<T> does follow the pattern but they could have simply introduced a Unit type in the BCL and replaced the non-generic Task type with Task<Unit> instead.

Microsoft could have added some unified Unit type but they did not, which makes this irrelevant.

Just because there is an obvious negative consequence to choosing a particular type schema (e.g. IEnumerable<t> extending IEnumerable) does not mean that the pattern is pointless. Sometimes biting the bullet is necessary since C# has a bullshit type system that makes dynamic orchestration a pain in the arse.


> Microsoft could have added some unified Unit type but they did not, which makes this irrelevant.

It's not irrelevant - you're using Task/Task<T> as an example of the non-generic base type/generic subtype pattern being necessary, but it isn't.

IEnumerable<T> extending IEnumerable is a consequence of C#1 not supporting generics at all, and the non-generic version would not be necessary if it had. I was just using it as a common example of the boilerplate the pattern incurs.


No, I was using IValidator, IValidator<t> as an example of a non generic base type being necessary when interfacing with existing libraries...

Honestly if you can that clojure library I linked a few posts ago without using this technique I will be extremeley impressed, but otherwise I disbelieve you. (Note: I have done this a few years ago.)




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: