/// <summary> /// Important method that wires and connects all the classes and interfaces together to run the application. /// If object A (this) has a private field of an interface, and object B implements the interface, then wire them together. Returns this for fluent style programming. /// ------------------------------------------------------------------------------------------------------------------ /// WireTo methods five important keys: /// 1. wires match interfaces, A (calls interface) and B (implements) /// 2. interfaces must be all private for matching to happen /// 3. can wire multiple matching interfaces /// 4. wires in order form top to bottom of not yet wired /// 5. can ovveride order by specifying port names as second parameter /// 6. looks for list as well (be careful of blocking other interfaces from wiring) /// ------------------------------------------------------------------------------------------------------------------ /// </summary> /// <param name="A"> /// The object on which the method is called is the object being wired from /// </param> /// <param name="B">The object being wired to (must implement the interface)</param> /// <returns></returns> /// <remarks> /// If A has two private fields of the same interface, the first compatible B object wired goes to the first one and the second compatible B object wired goes to the second. /// If A has multiple private interfaces of different types, only the first matching interface that B implements will be wired. /// In other words, by default, only one interface is wired between A and B /// To override this behaviour you can get give multiple interfaces in A a prefix "Pn_" where n is 0..9: /// Then a single wiring operation will wire all fieldnames with a consistent port prefix to the same B. /// These remarks apply only to single fields, not Lists. /// e.g. /// private IOneable client1Onabale; /// private ITwoable client1Twoable; /// private IThreeable client2; /// Clearly we want to wire two different clients. But if the first client wired implements all three interfaces, it will be wired to all three fields. /// So name the field like this: /// private IOneable P1_clientOnabale; /// private ITwoable P1_clientTwoable; /// private IThreeable P2_client; /// </remarks> public static T WireTo <T>(this T A, object B, string APortName = null, bool reverse = false) { string multiportExceptionMessage = $"The following wiring failed because the two instances are already wired together by another port."; multiportExceptionMessage += "\nPlease use a new WireTo with this additional port name specified:"; if (A == null) { throw new ArgumentException("A cannot be null"); } if (B == null) { throw new ArgumentException("B cannot be null"); } // achieve the following via reflection // A.field = (<type of interface>)B; // A.list.Add( (<type of interface>)B ); // Get the two instance name first for the Debug Output WriteLines var AinstanceName = A.GetType().GetProperties().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(A); if (AinstanceName == null) { AinstanceName = A.GetType().GetFields().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(A); } var BinstanceName = B.GetType().GetProperties().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(B); if (BinstanceName == null) { BinstanceName = B.GetType().GetFields().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(B); } var BType = B.GetType(); var AfieldInfos = A.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) .Where(f => (APortName == null || f.Name == APortName) && (!reverse ^ EndsIn_B(f.Name))).ToList(); // find the fields that the name meets all criteria // TODO: when not reverse ports ending in _B should be excluded //if (A.GetType().Name=="DragDrop" && AinstanceName=="Initial Tag") //{ // for breakpoint //} var wiredSomething = false; firstPortName = null; foreach (var BimplementedInterface in BType.GetInterfaces()) // consider every interface implemented by B { var AfieldInfo = AfieldInfos.FirstOrDefault(f => f.FieldType == BimplementedInterface && f.GetValue(A) == null); // find the first field in A that matches the interface type of B // TODO: the list case below should have the SamePort constraint as well // look for normal private fields first if (AfieldInfo != null) // there is a match { if (SamePort(AfieldInfo.Name)) { if (wiredSomething) { throw new Exception(multiportExceptionMessage); } AfieldInfo.SetValue(A, B); // do the wiring wiredSomething = true; } continue; // could be more than one interface to wire } // do the same as above for private fields that are a list of the interface of the matching type foreach (var AlistFieldInfo in AfieldInfos) { if (!AlistFieldInfo.FieldType.IsGenericType) //not matching interface { continue; } var AListFieldValue = AlistFieldInfo.GetValue(A); var AListGenericArguments = AlistFieldInfo.FieldType.GetGenericArguments(); if (AListGenericArguments.Length != 1) { continue; // A list should only have one type anyway } if (AListGenericArguments[0].IsAssignableFrom(BimplementedInterface)) // JRS: There was some case where == didn't work, maybe in the gamescoring application { if (AListGenericArguments[0] != BimplementedInterface) { var g = AListGenericArguments[0]; //if (g != typeof(object)) throw new Exception($"Different types {g} {AListGenericArguments[0]} {typeof(object)}"); continue; } if (AListFieldValue == null) { var listType = typeof(List <>); Type[] listParam = { BimplementedInterface }; AListFieldValue = Activator.CreateInstance(listType.MakeGenericType(listParam)); if (wiredSomething) { throw new Exception(multiportExceptionMessage); } AlistFieldInfo.SetValue(A, AListFieldValue); } AListFieldValue.GetType().GetMethod("Add").Invoke(AListFieldValue, new[] { B }); wiredSomething = true; break; } } } if (!reverse && !wiredSomething) { if (APortName != null) { // a specific port was specified so see if the port was already wired var AfieldInfo = AfieldInfos.FirstOrDefault(); if (AfieldInfo?.GetValue(A) != null) { throw new Exception($"Port already wired {A.GetType().Name}[{AinstanceName}].{APortName} to {BType.Name}[{BinstanceName}]"); } } //throw new Exception($"Failed to wire {A.GetType().Name}[{AinstanceName}].{APortName} to {BType.Name}[{BinstanceName}]"); } var method = A.GetType().GetMethod("PostWiringInitialize", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance); if (method != null) { InitializeDelegate handler = (InitializeDelegate)Delegate.CreateDelegate(typeof(InitializeDelegate), A, method); Initialize -= handler; // instances can be wired to/from more than once, so only register their PostWiringInitialize once Initialize += handler; } /* * method = B.GetType().GetMethod("PostWiringInitialize", System.Reflection.BindingFlags.NonPublic); * if (method != null) * { * InitializeDelegate handler = (InitializeDelegate)Delegate.CreateDelegate(typeof(InitializeDelegate), B, method); * Initialize += handler; * } */ return(A); }
public static T DeleteWireTo <T>(this T A, object B, string APortName = null, bool reverse = false) { if (A == null) { throw new ArgumentException("A cannot be null"); } if (B == null) { throw new ArgumentException("B cannot be null"); } // achieve the following via reflection // A.field = null; // A.list.Remove( (<type of interface>)B ); // Get the two instance name first for the Debug Output WriteLines var AinstanceName = A.GetType().GetProperties().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(A); if (AinstanceName == null) { AinstanceName = A.GetType().GetFields().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(A); } var BinstanceName = B.GetType().GetProperties().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(B); if (BinstanceName == null) { BinstanceName = B.GetType().GetFields().FirstOrDefault(f => f.Name == "instanceName")?.GetValue(B); } var BType = B.GetType(); var AfieldInfos = A.GetType().GetFields(System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance) .Where(f => (f.Name == APortName) && (!reverse ^ EndsIn_B(f.Name))).ToList(); // find the fields that the name meets all criteria foreach (var BimplementedInterface in BType.GetInterfaces()) // consider every interface implemented by B { var AfieldInfo = AfieldInfos.FirstOrDefault(f => f.FieldType == BimplementedInterface && f.GetValue(A) != null); // find the first field in A that matches the interface type of B // TODO: the list case below should have the SamePort constraint as well // look for normal private fields first if (AfieldInfo != null) // there is a match { if (SamePort(AfieldInfo.Name)) { AfieldInfo.SetValue(A, null); // do the wiring } continue; // could be more than one interface to wire } // do the same as above for private fields that are a list of the interface of the matching type foreach (var AlistFieldInfo in AfieldInfos) { if (!AlistFieldInfo.FieldType.IsGenericType) //not matching interface { continue; } var AListFieldValue = AlistFieldInfo.GetValue(A); var AListGenericArguments = AlistFieldInfo.FieldType.GetGenericArguments(); if (AListGenericArguments.Length != 1) { continue; // A list should only have one type anyway } if (AListGenericArguments[0].IsAssignableFrom(BimplementedInterface)) // JRS: There was some case where == didn't work, maybe in the gamescoring application { if (AListGenericArguments[0] != BimplementedInterface) { var g = AListGenericArguments[0]; if (g != typeof(object)) { throw new Exception("Different types"); } continue; } if (AListFieldValue == null) { var listType = typeof(List <>); Type[] listParam = { BimplementedInterface }; AListFieldValue = Activator.CreateInstance(listType.MakeGenericType(listParam)); AlistFieldInfo.SetValue(A, AListFieldValue); } AListFieldValue.GetType().GetMethod("Remove").Invoke(AListFieldValue, new[] { B }); break; } } } return(A); }