Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Check Not Implemented Members at Runtime

0.00/5 (No votes)
15 Jun 2017 1  
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:

//    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:

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 has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here