Readers of the Reactive Extensions series on this blog will have noticed recently that not all of the code always works* if you download the Rx assemblies and then copy the code off the blog. This is due to the quite frequent changes to the API. I personally have 7 different versions of the libraries and I don’t think I have downloaded them all as they have been released! Also the blog post were started all the way back in May 2010 so it is fair that there has been some movement in the API
*If you get the code from the http://code.google.com/p/rx-samples/source/checkout or as a zip file then it will work, as the correct version of the assembly is included.
For fun I thought I would try to exercise my LINQ skills and write a quick diff tool so I can see what on the public API is actually changing on me. I threw this class together in LinqPad
public class AssemblyDiff { private readonly string _oldAssemblyPath; private readonly string _newAssemblyPath; public AssemblyDiff(string oldAssemblyPath, string newAssemblyPath) { _oldAssemblyPath = oldAssemblyPath; _newAssemblyPath = newAssemblyPath; } public IEnumerable<String> NewMethodNames() { return MethodNameDelta(GetDeclaredMethods(_newAssemblyPath), GetDeclaredMethods(_oldAssemblyPath)); } public IEnumerable<String> DeprecatedMethodNames() { return MethodNameDelta(GetDeclaredMethods(_oldAssemblyPath), GetDeclaredMethods(_newAssemblyPath)); } public static IEnumerable<String> MethodNameDelta(IEnumerable<MethodInfo> original, IEnumerable<MethodInfo> modified) { return from methodName in original.Select(MethodName).Except(modified.Select(MethodName)) orderby methodName select methodName; } public IEnumerable<MethodInfo> NewMethods() { var oldMethods = GetDeclaredMethods(_oldAssemblyPath); var currentMethods = GetDeclaredMethods(_newAssemblyPath); return MethodDelta(oldMethods, currentMethods); } public IEnumerable<MethodInfo> DeprecatedMethods() { var oldMethods = GetDeclaredMethods(_oldAssemblyPath); var currentMethods = GetDeclaredMethods(_newAssemblyPath); return MethodDelta(currentMethods, oldMethods); } public static IEnumerable<MethodInfo> MethodDelta(IEnumerable<MethodInfo> original, IEnumerable<MethodInfo> changed) { var existingTypes = original.Select(m => m.ReflectedType.FullName) .Distinct() .ToList(); return from method in changed.Except(original, new MethodSignatureComparer()) where existingTypes.Contains(method.ReflectedType.FullName) orderby method.ReflectedType.Name, method.Name select method; } public IEnumerable<Type> NewTypes() { var currentTypes = GetTypes(_newAssemblyPath); var oldTypes = GetTypes(_oldAssemblyPath); return from type in currentTypes where !oldTypes.Select (t => t.FullName).Contains(type.FullName) select type; } public IEnumerable<Type> DeprecatedTypes() { var currentTypes = GetTypes(_newAssemblyPath); var oldTypes = GetTypes(_oldAssemblyPath); return from type in oldTypes where !currentTypes.Select (t => t.FullName).Contains(type.FullName) select type; } private static IEnumerable<MethodInfo> GetAllMethods(string path) { return from type in GetTypes(path) from method in type.GetMethods() where method.IsPublic select method; } private static IEnumerable<MethodInfo> GetDeclaredMethods(string path) { return GetAllMethods(path).Where(method => method.DeclaringType == method.ReflectedType); } private static IEnumerable<Type> GetTypes(string path) { return from file in Directory.EnumerateFiles(path, "*.dll") from module in Assembly.LoadFrom(file).GetModules() from type in module.GetTypes() where type.IsPublic select type; } private static string MethodName(MethodInfo m) { return string.Format("{0}.{1}", m.ReflectedType.Name, m.Name); } public static string MethodSignature(MethodInfo m) { //return m.ToString(); var ps = m.GetParameters(); var args = ps.Select(p=>ParameterSignature(p, ps.Length)); var argsDemlimted = string.Join(",", args); return string.Format("{0} {1}.{2}({3})", m.ReturnType.Name, m.ReflectedType.Name, m.Name, argsDemlimted); } private static string ParameterSignature(ParameterInfo parameter, int parameterCount) { var modifier = "";//out/ref/params/ var defaultValue = ""; if(parameter.IsOut) modifier = "out "; if(parameter.IsOptional) { modifier = "optional "; defaultValue = parameter.DefaultValue.ToString(); } if(parameter.IsRetval) modifier += "isretval "; if(parameter.IsIn) modifier += "IsIn "; if(parameter.IsLcid) modifier += "IsLcid "; if(parameter.Position== parameterCount-1 && parameter.ParameterType.IsArray) { modifier = "params "; } return string.Format("{0}{1}{2}", modifier,parameter.Name, defaultValue); } private class MethodSignatureComparer : IEqualityComparer<MethodInfo> { public bool Equals(MethodInfo lhs, MethodInfo rhs) { return string.Equals(lhs.ToString(), rhs.ToString()); } public int GetHashCode(MethodInfo method) { return method.ToString().GetHashCode(); } } }
and then I used it like this
var old = @"C:\Program Files\Microsoft Cloud Programmability\Reactive Extensions\v1.0.2787.0\Net4"; var current = @"C:\Program Files\Microsoft Cloud Programmability\Reactive Extensions\v1.0.2838.0\Net4"; var dllDiff = new AssemblyDiff(old, current); "New Methods".Dump(); dllDiff.NewMethodNames().Dump(); "Deprecated Methods".Dump(); dllDiff.DeprecatedMethodNames().Dump(); "New Types".Dump(); dllDiff.NewTypes().Select (t => t.FullName).Dump(); "Deprecated Types".Dump(); dllDiff.DeprecatedTypes().Select (t => t.FullName).Dump(); "New overloads".Dump(); dllDiff.NewMethods().Select(AssemblyDiff.MethodSignature).Dump(); "Deprecated overloads".Dump(); dllDiff.DeprecatedMethods().Select(AssemblyDiff.MethodSignature).Dump();
and I get this neat output.
New Methods (7 Items)
- ConnectableObservable`2.Connect
- ConnectableObservable`2.Subscribe
- Observable.GroupJoin
- Observable.Multicast
- Observable.Window
- Qbservable.GroupJoin
- Qbservable.Window
Deprecated Methods (6 Items)
- ConnectableObservable`1.Connect
- ConnectableObservable`1.Subscribe
- Observable.Prune
- Observable.Replay
- Qbservable.Prune
- Qbservable.Replay
New Types (1 Item)
- System.Collections.Generic.ConnectableObservable`2
Deprecated Types (0 Items)
New Overloads (30 Items)
- IEnumerable`1 EnumerableEx.Generate(initialState,condition,iterate,resultSelector)
- IObservable`1 Observable.BufferWithTime(source,timeSpan,timeShift,scheduler)
- IObservable`1 Observable.BufferWithTime(source,timeSpan,scheduler)
- IObservable`1 Observable.BufferWithTime(source,timeSpan,timeShift)
- IObservable`1 Observable.BufferWithTime(source,timeSpan)
- IObservable`1 Observable.BufferWithTimeOrCount(source,timeSpan,count,scheduler)
- IObservable`1 Observable.BufferWithTimeOrCount(source,timeSpan,count)
- IObservable`1 Observable.GroupJoin(left,right,leftDurationSelector,rightDurationSelector,resultSelector)
- IObservable`1 Observable.If(condition,thenSource)
- IObservable`1 Observable.Join(left,right,leftDurationSelector,rightDurationSelector,resultSelector)
- IConnectableObservable`1 Observable.Multicast(source,subject)
- IObservable`1 Observable.Publish(source,subject)
- IObservable`1 Observable.Publish(source,subject,selector)
- IObservable`1 Observable.Window(source,windowOpenings,windowClosingSelector)
- IObservable`1 Observable.Window(source,windowClosingSelector,scheduler)
- IObservable`1 Observable.Window(source,windowClosingSelector)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan,timeShift,scheduler)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan,scheduler)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan,timeShift)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan)
- IQbservable`1 Qbservable.BufferWithTimeOrCount(source,timeSpan,count,scheduler)
- IQbservable`1 Qbservable.BufferWithTimeOrCount(source,timeSpan,count)
- IQbservable`1 Qbservable.GroupJoin(left,right,leftDurationSelector,rightDurationSelector,resultSelector)
- IQbservable`1 Qbservable.If(provider,condition,thenSource)
- IQbservable`1 Qbservable.Join(left,right,leftDurationSelector,rightDurationSelector,resultSelector)
- IQbservable`1 Qbservable.Publish(source,subject,selector)
- IQbservable`1 Qbservable.Publish(source,subject)
- IQbservable`1 Qbservable.Window(source,windowOpenings,windowClosingSelector)
- IQbservable`1 Qbservable.Window(source,windowClosingSelector,scheduler)
- IQbservable`1 Qbservable.Window(source,windowClosingSelector)
Deprecated overloads (71 Items)
- IEnumerable`1 EnumerableEx.Generate(initialState,condition,resultSelector,iterate)
- IEnumerable`1 EnumerableEx.Generate(function)
- IEnumerable`1 EnumerableEx.Generate(initialState,resultSelector,iterate)
- IEnumerable`1 EnumerableEx.Generate(initial,resultSelector,iterate)
- IEnumerable`1 EnumerableEx.Generate(initial,condition,resultSelector,iterate)
- IObservable`1 Observable.BufferWithTime(source,timeSpan,timeShift,scheduler)
- IObservable`1 Observable.BufferWithTime(source,timeSpan,scheduler)
- IObservable`1 Observable.BufferWithTime(source,timeSpan,timeShift)
- IObservable`1 Observable.BufferWithTime(source,timeSpan)
- IObservable`1 Observable.BufferWithTimeOrCount(source,timeSpan,count,scheduler)
- IObservable`1 Observable.BufferWithTimeOrCount(source,timeSpan,count)
- IConnectableObservable`1 Observable.Prune(source)
- IConnectableObservable`1 Observable.Prune(source,scheduler)
- IObservable`1 Observable.Prune(source,selector)
- IObservable`1 Observable.Prune(source,selector,scheduler)
- IObservable`1 Observable.Publish(source1,source2,selector)
- IObservable`1 Observable.Publish(source1,source2,selector,scheduler)
- IObservable`1 Observable.Publish(source1,source2,source3,selector)
- IObservable`1 Observable.Publish(source1,source2,source3,selector,scheduler)
- IObservable`1 Observable.Publish(source1,source2,source3,source4,selector)
- IObservable`1 Observable.Publish(source1,source2,source3,source4,selector,scheduler)
- IConnectableObservable`1 Observable.Publish(source,initialValue)
- IConnectableObservable`1 Observable.Publish(source,initialValue,scheduler)
- IObservable`1 Observable.Publish(source,selector,initialValue)
- IObservable`1 Observable.Publish(source,selector,initialValue,scheduler)
- IConnectableObservable`1 Observable.Publish(source)
- IConnectableObservable`1 Observable.Publish(source,scheduler)
- IObservable`1 Observable.Publish(source,selector)
- IObservable`1 Observable.Publish(source,selector,scheduler)
- IConnectableObservable`1 Observable.Replay(source)
- IConnectableObservable`1 Observable.Replay(source,scheduler)
- IObservable`1 Observable.Replay(source,selector)
- IObservable`1 Observable.Replay(source,selector,scheduler)
- IConnectableObservable`1 Observable.Replay(source,window)
- IObservable`1 Observable.Replay(source,selector,window)
- IConnectableObservable`1 Observable.Replay(source,window,scheduler)
- IObservable`1 Observable.Replay(source,selector,window,scheduler)
- IConnectableObservable`1 Observable.Replay(source,bufferSize,scheduler)
- IObservable`1 Observable.Replay(source,selector,bufferSize,scheduler)
- IConnectableObservable`1 Observable.Replay(source,bufferSize)
- IObservable`1 Observable.Replay(source,selector,bufferSize)
- IConnectableObservable`1 Observable.Replay(source,bufferSize,window)
- IObservable`1 Observable.Replay(source,selector,bufferSize,window)
- IConnectableObservable`1 Observable.Replay(source,bufferSize,window,scheduler)
- IObservable`1 Observable.Replay(source,selector,bufferSize,window,scheduler)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan,timeShift,scheduler)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan,scheduler)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan,timeShift)
- IQbservable`1 Qbservable.BufferWithTime(source,timeSpan)
- IQbservable`1 Qbservable.BufferWithTimeOrCount(source,timeSpan,count,scheduler)
- IQbservable`1 Qbservable.BufferWithTimeOrCount(source,timeSpan,count)
- IQbservable`1 Qbservable.Prune(source,selector,scheduler)
- IQbservable`1 Qbservable.Prune(source,selector)
- IQbservable`1 Qbservable.Publish(source,selector,initialValue)
- IQbservable`1 Qbservable.Publish(source,selector,scheduler)
- IQbservable`1 Qbservable.Publish(source,selector,initialValue,scheduler)
- IQbservable`1 Qbservable.Publish(source,selector)
- IQbservable`1 Qbservable.Publish(source1,source2,selector)
- IQbservable`1 Qbservable.Publish(source1,source2,selector,scheduler)
- IQbservable`1 Qbservable.Publish(source1,source2,source3,selector)
- IQbservable`1 Qbservable.Publish(source1,source2,source3,selector,scheduler)
- IQbservable`1 Qbservable.Publish(source1,source2,source3,source4,selector)
- IQbservable`1 Qbservable.Publish(source1,source2,source3,source4,selector,scheduler)
- IQbservable`1 Qbservable.Replay(source,selector,bufferSize)
- IQbservable`1 Qbservable.Replay(source,selector,bufferSize,window)
- IQbservable`1 Qbservable.Replay(source,selector,bufferSize,window,scheduler)
- IQbservable`1 Qbservable.Replay(source,selector)
- IQbservable`1 Qbservable.Replay(source,selector,scheduler)
- IQbservable`1 Qbservable.Replay(source,selector,window)
- IQbservable`1 Qbservable.Replay(source,selector,window,scheduler)
- IQbservable`1 Qbservable.Replay(source,selector,bufferSize,scheduler)
From memory Generate has been a constant source of change which has confused some readers that are using different versions of the library to what the Part 2 post was done with. This diff script goes to show that it is still undergoing changes
Links: