2
\$\begingroup\$

While working on one of my projects I actively used Reflection. While working with class Type I expected methods: TryGetMember, TryGetProperty, TryGetField, TryGetMethod. I've implemented them as extension methods:

public static class TypeExtensions
{
    private const BindingFlags DEFAULT_LOOKUP = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;

    /// <summary>
    /// Searches for the specified members, using the specified binding constraints.
    /// </summary>
    /// <param name="name">The string containing the name of the members to get.</param>
    /// <param name="bindingAttr">
    /// A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    /// how the search is conducted.-or- Zero, to return an empty array.
    /// </param>
    /// <param name="result">When this method returns, contains array of System.Reflection.MemberInfo with found members or empty (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if method with the given data was found; otherwise false.</returns>
    public static bool TryGetMember(this Type type, string name, BindingFlags bindingAttr, out MemberInfo[] result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");

        result = new MemberInfo[0];

        try
        {
            result = type.GetMember(name, bindingAttr);
        }
        catch
        {
        }

        return result.Count() != 0;
    }

    /// <summary>
    /// Searches for the public members with the specified name.
    /// </summary>
    /// <param name="name">The string containing the name of the public members to get.</param>
    /// <param name="result">When this method returns, contains array of System.Reflection.MemberInfo with found members or empty (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if method with the given data was found; otherwise false.</returns>
    public static bool TryGetMember(this Type type, string name, out MemberInfo[] result)
    {
        return type.TryGetMember(name, DEFAULT_LOOKUP, out result);
    }

    /// <summary>
    /// Searches for the specified property, using the specified binding constraints.
    /// </summary>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="bindingAttr">
    /// A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    /// how the search is conducted.-or- Zero, to return null.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, BindingFlags bindingAttr, out PropertyInfo result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");

        result = default(PropertyInfo);

        try
        {
            result = type.GetProperty(name, bindingAttr);
        }
        catch
        {
        }

        return result != default(PropertyInfo);
    }

    /// <summary>
    /// Searches for the public property with the specified name.
    /// </summary>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, out PropertyInfo result)
    {
        return type.TryGetProperty(name, DEFAULT_LOOKUP, out result);
    }

    /// <summary>
    /// Searches for the specified public property whose parameters match the specified
    /// argument types.
    /// </summary>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="returnType">The return type of the property.</param>
    /// <param name="types">
    /// An array of System.Type objects representing the number, order, and type
    /// of the parameters for the indexed property to get.-or- An empty array of
    /// the type System.Type (that is, Type[] types = new Type[0]) to get a property
    /// that is not indexed.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, Type returnType, Type[] types, out PropertyInfo result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");
        if (returnType == null)
            throw new ArgumentNullException("returnType");

        result = default(PropertyInfo);

        try
        {
            result = type.GetProperty(name, returnType, types);
        }
        catch
        {
        }

        return result != default(PropertyInfo);
    }

    /// <summary>
    /// Searches for the public property with the specified name and return type.
    /// </summary>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="returnType">The return type of the property.</param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, Type returnType, out PropertyInfo result)
    {
        return type.TryGetProperty(name, returnType, null, out result);
    }

    /// <summary>
    /// Searches for the specified field, using the specified binding constraints.
    /// </summary>
    /// <param name="name">The string containing the name of the data field to get.</param>
    /// <param name="bindingAttr">
    ///  A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    ///  how the search is conducted.-or- Zero, to return null.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.FieldInfo of found data field or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if data field with the given data was found; otherwise false.</returns>
    public static bool TryGetField(this Type type, string name, BindingFlags bindingAttr, out FieldInfo result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");

        result = default(FieldInfo);

        try
        {
            result = type.GetField(name, bindingAttr);
        }
        catch
        {
        }

        return result != default(FieldInfo);
    }

    /// <summary>
    /// Searches for the public field with the specified name.
    /// </summary>
    /// <param name="name">The string containing the name of the data field to get.</param>
    /// <param name="result">When this method returns, contains System.Reflection.FieldInfo of found data field or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if data field with the given data was found; otherwise false.</returns>
    public static bool TryGetField(this Type type, string name, out FieldInfo result)
    {
        return type.TryGetField(name, DEFAULT_LOOKUP, out result);
    }

    /// <summary>
    /// When overridden in a derived class, returns the System.Reflection.EventInfo
    /// object representing the specified event, using the specified binding constraints.
    /// </summary>
    /// <param name="name">
    /// The string containing the name of an event which is declared or inherited
    /// by the current System.Type.
    /// </param>
    /// <param name="bindingAttr">
    ///  A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    ///  how the search is conducted.-or- Zero, to return null. 
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.EventInfo of found event or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if event with the given data was found; otherwise false.</returns>
    public static bool TryGetEvent(this Type type, string name, BindingFlags bindingAttr, out EventInfo result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");

        result = default(EventInfo);

        try
        {
            result = type.GetEvent(name, bindingAttr);
        }
        catch
        {
        }

        return result != default(EventInfo);
    }

    /// <summary>
    ///  Returns the System.Reflection.EventInfo object representing the specified
    ///  public event.
    /// </summary>
    /// <param name="name">
    ///  The string containing the name of an event that is declared or inherited
    ///  by the current System.Type.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.EventInfo of found event or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if event with the given data was found; otherwise false.</returns>
    public static bool TryGetEvent(this Type type, string name, out EventInfo result)
    {
        return type.TryGetEvent(name, DEFAULT_LOOKUP, out result);
    }

    /// <summary>
    /// Searches for the specified method, using the specified binding constraints.
    /// </summary>
    /// <param name="name">The string containing the name of the method to get.</param>
    /// <param name="bindingAttr">
    /// A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    /// how the search is conducted.-or- Zero, to return null.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.MethodInfo of found method or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if method with the given data was found and match is not ambiguous; otherwise false.</returns>
    public static bool TryGetMethod(this Type type, string name, BindingFlags bindingAttr, out MethodInfo result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");

        result = default(MethodInfo);

        try
        {
            result = type.GetMethod(name, bindingAttr);
        }
        catch
        {
        }

        return result != default(MethodInfo);
    }

    /// <summary>
    /// Searches for the public method with the specified name.
    /// </summary>
    /// <param name="name">The string containing the name of the public method to get.</param>
    /// <param name="result">When this method returns, contains System.Reflection.MethodInfo of found method or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if method with the given data was found and match is not ambiguous; otherwise false.</returns>
    public static bool TryGetMethod(this Type type, string name, out MethodInfo result)
    {
        return type.TryGetMethod(name, DEFAULT_LOOKUP, out result);
    }

    /// <summary>
    /// Searches for the specified public method whose parameters match the specified
    /// argument types.
    /// </summary>
    /// <param name="name">The string containing the name of the public method to get.</param>
    /// <param name="types">
    ///  An array of System.Type objects representing the number, order, and type
    ///  of the parameters for the method to get.-or- An empty array of System.Type
    ///  objects (as provided by the System.Type.EmptyTypes field) to get a method
    ///  that takes no parameters.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.MethodInfo of found method or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"></exception>
    /// <returns>returns true if method with the given data was found; otherwise false.</returns>
    public static bool TryGetMethod(this Type type, string name, Type[] types, out MethodInfo result)
    {
        if (type == null)
            throw new ArgumentNullException("type");
        if (name == null)
            throw new ArgumentNullException("name");

        result = default(MethodInfo);

        try
        {
            result = type.GetMethod(name, types);
        }
        catch
        {
        }

        return result != default(MethodInfo);
    }
}

I know that there [almost] always is a reason why such simple and useful (at least, I think so) features are missing. Is there anything wrong (i.e.: some edge-cases that are not handled, leading to bad coding practices, etc.) with this or any other implementation of these methods? Also how can this code improved?

\$\endgroup\$
3
\$\begingroup\$

In the first TryGetMember(), I'd change the last line from return result.Count() != 0; to return result.Length != 0;. It's the only code that references LINQ and it's not really even needed there. If you feel compelled to use LINQ, that line would also be better written as return result.Any();.

(adding on)

So, in most of your methods, you're assigning to result twice for the happy path when you don't need to. Move the default assignment into your empty catch clauses and you'll have better performance overall and neater control code.

Also, your XML documentation is missing the type parameter on every method plus any exception descriptions.

So, I've gussied it up as described and here's the result:

public static class TypeExtensions
{
    private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;

    /// <summary>
    /// Searches for the specified members, using the specified binding constraints.
    /// </summary>
    /// <param name="type">The type whose members to get.</param>
    /// <param name="name">The string containing the name of the members to get.</param>
    /// <param name="bindingAttr">
    /// A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    /// how the search is conducted.-or- Zero, to return an empty array.
    /// </param>
    /// <param name="result">When this method returns, contains array of System.Reflection.MemberInfo with found members or empty (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if method with the given data was found; otherwise false.</returns>
    public static bool TryGetMember(this Type type, string name, BindingFlags bindingAttr, out MemberInfo[] result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        try
        {
            result = type.GetMember(name, bindingAttr);
        }
        catch
        {
            result = new MemberInfo[0];
        }

        return result.Length != 0;
    }

    /// <summary>
    /// Searches for the public members with the specified name.
    /// </summary>
    /// <param name="type">The type whose members to get.</param>
    /// <param name="name">The string containing the name of the public members to get.</param>
    /// <param name="result">When this method returns, contains array of System.Reflection.MemberInfo with found members or empty (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if method with the given data was found; otherwise false.</returns>
    public static bool TryGetMember(this Type type, string name, out MemberInfo[] result)
    {
        return type.TryGetMember(name, DefaultLookup, out result);
    }

    /// <summary>
    /// Searches for the specified property, using the specified binding constraints.
    /// </summary>
    /// <param name="type">The type whose properties to get.</param>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="bindingAttr">
    /// A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    /// how the search is conducted.-or- Zero, to return null.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, BindingFlags bindingAttr, out PropertyInfo result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        try
        {
            result = type.GetProperty(name, bindingAttr);
        }
        catch
        {
            result = default(PropertyInfo);
        }

        return result != default(PropertyInfo);
    }

    /// <summary>
    /// Searches for the public property with the specified name.
    /// </summary>
    /// <param name="type">The type whose properties to get.</param>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, out PropertyInfo result)
    {
        return type.TryGetProperty(name, DefaultLookup, out result);
    }

    /// <summary>
    /// Searches for the specified public property whose parameters match the specified
    /// argument types.
    /// </summary>
    /// <param name="type">The type whose properties to get.</param>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="returnType">The return type of the property.</param>
    /// <param name="types">
    /// An array of System.Type objects representing the number, order, and type
    /// of the parameters for the indexed property to get.-or- An empty array of
    /// the type System.Type (that is, Type[] types = new Type[0]) to get a property
    /// that is not indexed.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="returnType"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, Type returnType, Type[] types, out PropertyInfo result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        if (returnType == null)
        {
            throw new ArgumentNullException("returnType");
        }

        try
        {
            result = type.GetProperty(name, returnType, types);
        }
        catch
        {
            result = default(PropertyInfo);
        }

        return result != default(PropertyInfo);
    }

    /// <summary>
    /// Searches for the public property with the specified name and return type.
    /// </summary>
    /// <param name="type">The type whose properties to get.</param>
    /// <param name="name">The string containing the name of the property to get.</param>
    /// <param name="returnType">The return type of the property.</param>
    /// <param name="result">When this method returns, contains System.Reflection.PropertyInfo of found property or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="returnType"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if property with the given data was found; otherwise false.</returns>
    public static bool TryGetProperty(this Type type, string name, Type returnType, out PropertyInfo result)
    {
        return type.TryGetProperty(name, returnType, null, out result);
    }

    /// <summary>
    /// Searches for the specified field, using the specified binding constraints.
    /// </summary>
    /// <param name="type">The type whose fields to get.</param>
    /// <param name="name">The string containing the name of the data field to get.</param>
    /// <param name="bindingAttr">
    ///  A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    ///  how the search is conducted.-or- Zero, to return null.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.FieldInfo of found data field or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if data field with the given data was found; otherwise false.</returns>
    public static bool TryGetField(this Type type, string name, BindingFlags bindingAttr, out FieldInfo result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        try
        {
            result = type.GetField(name, bindingAttr);
        }
        catch
        {
            result = default(FieldInfo);
        }

        return result != default(FieldInfo);
    }

    /// <summary>
    /// Searches for the public field with the specified name.
    /// </summary>
    /// <param name="type">The type whose fields to get.</param>
    /// <param name="name">The string containing the name of the data field to get.</param>
    /// <param name="result">When this method returns, contains System.Reflection.FieldInfo of found data field or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if data field with the given data was found; otherwise false.</returns>
    public static bool TryGetField(this Type type, string name, out FieldInfo result)
    {
        return type.TryGetField(name, DefaultLookup, out result);
    }

    /// <summary>
    /// When overridden in a derived class, returns the System.Reflection.EventInfo
    /// object representing the specified event, using the specified binding constraints.
    /// </summary>
    /// <param name="type">The type whose events to get.</param>
    /// <param name="name">
    /// The string containing the name of an event which is declared or inherited
    /// by the current System.Type.
    /// </param>
    /// <param name="bindingAttr">
    ///  A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    ///  how the search is conducted.-or- Zero, to return null. 
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.EventInfo of found event or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if event with the given data was found; otherwise false.</returns>
    public static bool TryGetEvent(this Type type, string name, BindingFlags bindingAttr, out EventInfo result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        try
        {
            result = type.GetEvent(name, bindingAttr);
        }
        catch
        {
            result = default(EventInfo);
        }

        return result != default(EventInfo);
    }

    /// <summary>
    ///  Returns the System.Reflection.EventInfo object representing the specified
    ///  public event.
    /// </summary>
    /// <param name="type">The type whose events to get.</param>
    /// <param name="name">
    ///  The string containing the name of an event that is declared or inherited
    ///  by the current System.Type.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.EventInfo of found event or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if event with the given data was found; otherwise false.</returns>
    public static bool TryGetEvent(this Type type, string name, out EventInfo result)
    {
        return type.TryGetEvent(name, DefaultLookup, out result);
    }

    /// <summary>
    /// Searches for the specified method, using the specified binding constraints.
    /// </summary>
    /// <param name="type">The type whose events to get.</param>
    /// <param name="name">The string containing the name of the method to get.</param>
    /// <param name="bindingAttr">
    /// A bitmask comprised of one or more System.Reflection.BindingFlags that specify
    /// how the search is conducted.-or- Zero, to return null.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.MethodInfo of found method or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if method with the given data was found and match is not ambiguous; otherwise false.</returns>
    public static bool TryGetMethod(this Type type, string name, BindingFlags bindingAttr, out MethodInfo result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        try
        {
            result = type.GetMethod(name, bindingAttr);
        }
        catch
        {
            result = default(MethodInfo);
        }

        return result != default(MethodInfo);
    }

    /// <summary>
    /// Searches for the public method with the specified name.
    /// </summary>
    /// <param name="type">The type whose methods to get.</param>
    /// <param name="name">The string containing the name of the public method to get.</param>
    /// <param name="result">When this method returns, contains System.Reflection.MethodInfo of found method or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if method with the given data was found and match is not ambiguous; otherwise false.</returns>
    public static bool TryGetMethod(this Type type, string name, out MethodInfo result)
    {
        return type.TryGetMethod(name, DefaultLookup, out result);
    }

    /// <summary>
    /// Searches for the specified public method whose parameters match the specified
    /// argument types.
    /// </summary>
    /// <param name="type">The type whose methods to get.</param>
    /// <param name="name">The string containing the name of the public method to get.</param>
    /// <param name="types">
    ///  An array of System.Type objects representing the number, order, and type
    ///  of the parameters for the method to get.-or- An empty array of System.Type
    ///  objects (as provided by the System.Type.EmptyTypes field) to get a method
    ///  that takes no parameters.
    /// </param>
    /// <param name="result">When this method returns, contains System.Reflection.MethodInfo of found method or null (in a case of failure).</param>
    /// <exception cref="ArgumentNullException"><paramref name="type"/> may not be <c>null</c>.</exception>
    /// <exception cref="ArgumentNullException"><paramref name="name"/> may not be <c>null</c>.</exception>
    /// <returns>returns true if method with the given data was found; otherwise false.</returns>
    public static bool TryGetMethod(this Type type, string name, Type[] types, out MethodInfo result)
    {
        if (type == null)
        {
            throw new ArgumentNullException("type");
        }

        if (name == null)
        {
            throw new ArgumentNullException("name");
        }

        try
        {
            result = type.GetMethod(name, types);
        }
        catch
        {
            result = default(MethodInfo);
        }

        return result != default(MethodInfo);
    }
}
\$\endgroup\$
  • 1
    \$\begingroup\$ Nice review. Reducing dependencies is definitely great idea. There's no need in using Linq here so this class will become compatible with old .NET versions. And big thanks for improving documentation (I really suck at writing docs). I'll wait for another reviews (if there are any). If nobody will post better review, I'll accept this soon. \$\endgroup\$ – Leri May 24 '13 at 15:01
0
\$\begingroup\$

Read the documentation

  • No need for the try-catch blocks
  • Every method returns a fix type if something goes wrong
public static class TypeExtensions
{
    private const BindingFlags DefaultLookup = BindingFlags.Instance | BindingFlags.Static | BindingFlags.Public;

    private static void CheckNameArgument(string name)
    {
        if (name == null)
            throw new ArgumentNullException("name");
    }

    private static void CheckTypeArgument(Type type)
    {
        if (type == null)
            throw new ArgumentNullException("type");
    }

    private static void CheckReturnTypeArgument(Type returnType)
    {
        if (returnType == null)
            throw new ArgumentNullException("returnType");
    }

    public static bool TryGetMember(this Type type, string name, BindingFlags bindingAttr, out MemberInfo[] result)
    {
        CheckTypeArgument(type);
        CheckNameArgument(name);

        result = type.GetMembers(bindingAttr).Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).ToArray();

        return result.Any();
    }

    public static bool TryGetMember(this Type type, string name, out MemberInfo[] result)
    {
        return type.TryGetMember(name, DefaultLookup, out result);
    }

    public static bool TryGetProperty(this Type type, string name, BindingFlags bindingAttr, out PropertyInfo result)
    {
        CheckTypeArgument(type);
        CheckNameArgument(name);
        var props = type.GetProperties(bindingAttr).Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).ToArray();

        result = props.Length == 1 ? props[0] : null;

        return props.Length == 1;
    }

    public static bool TryGetProperty(this Type type, string name, out PropertyInfo result)
    {
        return type.TryGetProperty(name, DefaultLookup, out result);
    }

    public static bool TryGetProperty(this Type type, string name, Type returnType, Type[] types, out PropertyInfo result)
    {
        //TODO:types?

        CheckTypeArgument(type);
        CheckNameArgument(name);
        CheckReturnTypeArgument(returnType);
        var properties = type.GetProperties()
                            .Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
                                && x.PropertyType == returnType)
                            .ToArray();

        result = properties.Length == 1 ? properties[0] : null;

        return properties.Length == 1;
    }

    public static bool TryGetProperty(this Type type, string name, Type returnType, out PropertyInfo result)
    {
        return type.TryGetProperty(name, returnType, null, out result);
    }

    public static bool TryGetField(this Type type, string name, BindingFlags bindingAttr, out FieldInfo result)
    {
        CheckTypeArgument(type);
        CheckNameArgument(name);
        var fields = type.GetFields(bindingAttr).Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).ToArray();
        result = fields.Length == 1 ? fields[0] : null;

        return fields.Length == 1;
    }

    public static bool TryGetField(this Type type, string name, out FieldInfo result)
    {
        return type.TryGetField(name, DefaultLookup, out result);
    }

    public static bool TryGetEvent(this Type type, string name, BindingFlags bindingAttr, out EventInfo result)
    {
        CheckTypeArgument(type);
        CheckNameArgument(name);
        var events = type.GetEvents(bindingAttr).Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)).ToArray();
        result = events.Length == 1 ? events[0] : null;

        return events.Length == 1;
    }

    public static bool TryGetEvent(this Type type, string name, out EventInfo result)
    {
        return type.TryGetEvent(name, DefaultLookup, out result);
    }

    public static bool TryGetMethod(this Type type, string name, BindingFlags bindingAttr, out MethodInfo result)
    {
        CheckTypeArgument(type);
        CheckNameArgument(name);
        var methods = type.GetMethods(bindingAttr)
                        .Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase))
                        .ToArray();

        result = methods.Length == 1 ? methods[0] : null;

        return methods.Length == 1;
    }

    public static bool TryGetMethod(this Type type, string name, out MethodInfo result)
    {
        return type.TryGetMethod(name, DefaultLookup, out result);
    }

    public static bool TryGetMethod(this Type type, string name, Type[] types, out MethodInfo result)
    {
        CheckTypeArgument(type);
        CheckNameArgument(name);
        var methods = type.GetMethods()
                            .Where(x => x.Name.Equals(name, StringComparison.InvariantCultureIgnoreCase)
                                && x.GetParameters().All(y => types.Contains(y.ParameterType)))
                            .ToArray();
        result = methods.Length == 1 ? methods[0] : null;

        return methods.Length == 1;
    }
}

Documentation

The documentation is completely unnecessary i think everthing is speaks for it's self.

\$\endgroup\$
  • \$\begingroup\$ I want to ignore ambiguous matches as well that's why it's in try catch. \$\endgroup\$ – Leri May 24 '13 at 18:38
  • \$\begingroup\$ I've made an edit to my answer the try-catch blocks are still unnecessary and only one TODO is left becouse i don't know how to apply that Type[] in the search. \$\endgroup\$ – Peter Kiss May 24 '13 at 23:46
  • \$\begingroup\$ But you create dependency on linq that makes code incompatible with old versions of .NET framework. Also your code will have more overhead than try/catch (Actually, you managed to make O(1) problem O(n)) . And finally, it's true that most of us hate writing docs, but they are necessary and should always be written. Otherwise, you, as a developer, will expect method doing something but won't know if your expectations are correct. \$\endgroup\$ – Leri May 25 '13 at 6:41
  • \$\begingroup\$ Try-catch blocks doesn't have overload but exceptions do. The Linq can be replaced by for cycles everywhere and we don't have to worry about O(1) vs. O(n) problem when finding the correct methods/properties/fields becouse reflection is slow as hell. \$\endgroup\$ – Peter Kiss May 25 '13 at 7:54
  • 1
    \$\begingroup\$ When the CLR looking after matching properties/methods/fileds it will also iterate through at least 2 times over the found values if you are looking after one property (GetProerty method for example). So if we first collect all possible values manually we are not creating any overhead. But if we expecting one value (GetProperty) and the CLR found multiple value it has to throw an Exception which has a huge overhead check out with profiling. \$\endgroup\$ – Peter Kiss May 25 '13 at 9:58

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.