Click here to Skip to main content
15,867,686 members
Articles / DevOps
Tip/Trick

Check Not Implemented Members at Runtime

Rate me:
Please Sign up or sign in to vote.
4.67/5 (4 votes)
15 Jun 2017CPOL 7.4K   6   2
Create default interface implementation or whatever you want with runtime checking not implemented members

Introduction

Interface implementation on Visual Studio adds initial "throw new NotIImplementedException();" on each interface member. This is true in other refactoring scenarios.

Usually, a programmer must change each "throw new NotImplementedException()" manually.

In this tip, I show how to detect a "throw ..." before calling the member.

Background

Using Reflection's MemberInfo, we can obtain a MethodBody class.

MethodBody class can give us early IL codes without executing them and without special privileges.

Any "throw new NotImplemented();" is translated as a 6Bytes IL code starting in 115 (IL for new) and ending with 122 (IL for throw). Additional checking ensures no mistakes.

Using the Code

This is an example where we can override not implemented methods on runtime with new functions:

C#
//    public interface IMyInterface
//    {
//        Func<L, L> FuncCreator { get; }
//        Action<L> BeginUpdate { get; }
//        Action<L> EndUpdate { get; }
//    }
//
//    public class MyClass:IMyInterface
//    {
//      Func<L,L> FuncCreator=>throw new NotImplementedException();
//      Action<L> BeginUpdate=>throw new NotImplementedException();
//      Action<L> EndUpdate=>throw new NotImplementedException();
//     } 

// Now we can cache those actions by class with a default action if not implemented.

var FuncCreator = MyClass.IsNotImplemented
	(nameof(MyClass.FuncCreator)) ? null : MyClass.FuncCreator;
var BeginUpdate = MyClass.IsNotImplemented
	(nameof(MyClass.BeginUpdate)) ? null : MyClass.BeginUpdate; 
var EndUpdate = fake.IsNotImplemented
	(nameof(MyClass.EndUpdate)) ? null : MyClass.EndUpdate;

This magic is possible via Reflection:

C#
public static class Helper{

        public static bool IsNotImplementedMember(this MemberInfo me)
        {
            switch (me)
            {
                case MethodInfo mi:
                    var mb = mi.GetMethodBody();
                    if (mb == null) return true;
                    if (mb.ExceptionHandlingClauses.Any()) return false;
                    if (mb.InitLocals) return false;
                    if (mb.LocalVariables.Any()) return false;
                    var il = mb.GetILAsByteArray().SkipWhile(b => b == 0).ToArray();
                    if (il.Length != 6) return false;
                    return (il[0] == 115) && (il[5] == 122);
                case PropertyInfo pro:
                    return IsNotImplementedGetter(pro);
                default:
                    return false;
            }
        }
        public static bool IsNotImplementedGetter(this PropertyInfo mi)
        {
            var m = mi.GetGetMethod();
            if (m == null) return true;
            return m.IsNotImplementedMember();
        }
        public static bool IsNotImplementedSetter(this PropertyInfo mi)
        {
            var m = mi.GetSetMethod();
            if (m == null) return true;
            return m.IsNotImplementedMember();
        }
        public static bool IsNotImplemented
        (Type t, string name, bool @public = true, bool instance = true)
        {
            BindingFlags b = (@public ? BindingFlags.Public : 
            BindingFlags.NonPublic) | 
            (instance ? BindingFlags.Instance : BindingFlags.Static);
            var m = t.GetMember(name, b);
            if (!m.Any()) return false;
            if (m.Length != 1) throw new ArgumentException
            ("More than one member same name");
            return IsNotImplementedMember(m[0]);
        }
        public static bool IsNotImplemented<T>
        (string name, bool @public = true, bool instance = true)
        {
            return IsNotImplemented(typeof(T), name, @public, instance);
        }
        public static bool IsNotImplemented<T>
        (this T obj,string name, bool @public = true, bool instance = true)
        {
            return IsNotImplemented(obj.GetType(), name, @public, instance);
        }
        public static bool IsNotImplemented<T>(Expression<Action<T>> expression)
        {
            var me = ExtractMember(expression);
            return IsNotImplemented<T>(me.Name);
        }
        public static bool IsNotImplemented<T>
        (this T obj,Expression<Action<T>> expression)
        {
            var me = ExtractMember(expression);
            return IsNotImplemented<T>(me.Name);
        }
}

This Extension Helper Class has several entry points. I use "nameof(...)" but there are other ways.

Points of Interest

As simple as checking IL implementation. Instead, I would have preferred default interface implementations but, at the current time, C# lacks this.

History

  • First version

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
Spain Spain
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionClever, but ... Pin
John Brett21-Jun-17 6:02
John Brett21-Jun-17 6:02 
GeneralMy vote of 5 Pin
MichelChauveau16-Jun-17 1:03
MichelChauveau16-Jun-17 1:03 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.