Measuring relational inheritance distance between types - c#

Does anyone know how I could (or if there is an existing algorithm) measure the relational distance between two .NET types?
By this I mean the number of 'steps' in the hierarchical tree that is required to get from object A to object B.
For example, if object A is a Button, and object B is a LinkButton, there would be 2 steps, Button -> WebControl -> LinkButton. Would I need to create my own static inheritance tree and use a path finding algorithm, or is there a way I can dynamically look at the inheritance structure of .NET to calculate the distance between two objects?

Non-generic way (also you don't have to specify parent/child explicitly):
private static int CalulateDistanceOneWay(Type firstType, Type secondType)
{
var chain = new List<Type>();
while (firstType != typeof(object))
{
chain.Add(firstType);
firstType = firstType.BaseType;
}
return chain.IndexOf(secondType);
}
// returns -1 for invalid input, distance between types otherwise
public static int CalculateDistance(Type firstType, Type secondType)
{
int result = CalulateDistanceOneWay(firstType, secondType);
if (result >= 0)
{
return result;
}
return CalulateDistanceOneWay(secondType, firstType);
}
EDIT: update to calculate cousins:
public class DistanceResult
{
public Type SharedAncestor { get; private set; }
public int FirstTypeDistance { get; private set; }
public int SecondTypeDistance { get; private set; }
public DistanceResult(Type sharedAncestor, int firstTypeDistance, int secondTypeDistance)
{
SharedAncestor = sharedAncestor;
FirstTypeDistance = firstTypeDistance;
SecondTypeDistance = secondTypeDistance;
}
}
static DistanceResult CalculateDistance(Type firstType, Type secondType)
{
var firstChain = new List<Type>();
while (firstType != typeof(object))
{
firstChain.Add(firstType);
firstType = firstType.BaseType;
}
firstChain.Add(typeof(object));
var secondChain = new List<Type>();
while(secondType != typeof(object))
{
secondChain.Add(secondType);
secondType = secondType.BaseType;
}
secondChain.Add(typeof(object));
for(var i = 0; i < secondChain.Count; i++)
{
var type = secondChain[i];
int index = firstChain.IndexOf(type);
if (index >= 0)
{
return new DistanceResult(firstChain[index], index, i);
}
}
return null;
}

You can walk through an inheritance path using Type.BaseType. For example:
public static int GetTypeDistance<T, B>(T t, B baseType)
{
if (t is B) // checking if t inherits baseType
{
int distance = 0;
Type curType = t.GetType();
while (curType != typeof(B) && curType != null)
{
distance++;
curType = curType.BaseType;
}
return distance;
}
else { throw new Exception("..."); }
}

Based on the answers from Ondrej and Bényi here are two extension methods to compute the distance from a particular type to either its (indirect) base class type or one of the interfaces implemented by it or one of its base classes.
Usage examples:
Assert.AreEqual( 4, typeof( MultiDictionary<int, int> ).DistanceTo<IEnumerable>() );
Assert.AreEqual( 4, typeof( MultiDictionary<int, int> ).DistanceTo( typeof( IEnumerable ) );
Assert.AreEqual( 2, typeof( StringReader ).DistanceTo( typeof( IDisposable ) ) );
Extension methods:
public static class ExtensionsForType
{
public static int DistanceTo( [CanBeNull] this Type current, [NotNull] Type target )
{
Contract.Requires<ArgumentNullException>( target != null );
// `root` will point to the topmost type which is implementing
// our `target` interface
Type root = current;
// search for topmost base type implementing `target` interface type
// or for the `target` base class type itself
int distance = 0;
while ( current != null && ( target.IsInterface
? current.GetInterfaces().Contains( target )
: current != target ) )
{
root = current;
current = current.BaseType;
distance++;
}
// probably the `current` type does not even derive from / implement
// the target type at all
if ( current == null ) return -1;
// if it's not an interface then we've found it in one of the base classes
if ( !target.IsInterface ) return distance;
// go one step back, because 'current' does not implement
// our target interface anymore
distance--;
// iterate interface "derivations" while the target interface is still
// in the list of implemented interfaces
Type[] interfaces = root.GetInterfaces();
while ( interfaces.Contains( target ) )
{
interfaces = interfaces.SelectMany( i => i.GetInterfaces() ).ToArray();
distance++;
}
return distance;
}
public static int DistanceTo<T>( [CanBeNull] this Type current )
{
return current.DistanceTo( typeof( T ) );
}
}

Related

assert that all fields in 2 objects are the same c#

I am doing unit testing, and basically want to check that the data that 2 objects hold is the same
Assert.AreEqual(object1, object2);
Assert.IsTrue(object1.Equals(object2)); //this of course doesn't work
I am searching for the C# equivalent of assertJ
Assert.That(object1).isEqualToComparingFieldByField(object2)
You could either use records (c# 9 +) or you have to override the Equals method (if you have access and you can change the objects that you're working with).
Records example:
var point = new Point(3, 4);
var point2 = new Point(3, 4);
var test = point.Equals(point2); //this is true
public record Point(int X, int Y);
with classes:
public class Point
{
public int X { get; }
public int Y { get; }
public override bool Equals(object? obj)
{
if (obj == null)
return false;
return obj is Point point && (point.X == X && point.Y == Y);
}
public override int GetHashCode()
{
return HashCode.Combine(X, Y);
}
}
if you are not allowed to touch the implementation, then you could use serialization and compare the strings:
var obj1Str = JsonConvert.SerializeObject(object1);
var obj2Str = JsonConvert.SerializeObject(object2);
Assert.Equal(obj1Str, obj2Str);
using Newtonsoft.Json nuget
C# classes are reference equality, which means that variables are the same using the standard Equals and == if they point to the same object, you could override that behaivour, but it may break something now or in the future.
Or, you could switch to using a construct that's value equality by default, which structs as well as record classes are. If you can't (or don't want to) do that you can implement a value equals "helper" method yourself. I would not recommend overriding the Equals method or the == operator, as that can (and most likely will) lead to errors in the future instead I recommend you write your own ValueEquals method or extension method, something along the lines of
class Foo
{
public int Count {get; set;}
public string Message {get; set;}
}
public static bool ValueEquals(this Foo self, Foo other)
{
return self.Count == other.Count && self.Message == other.Message;
}
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(myFoo1.ValueEquals(myFoo2));
}
Depending on whether or not you can/ want to add a ValueEquals to your Foo class you can decide on doing it with an extension method or a normal method.
You could also implement a IEqualityComparer<T> like
public class FooValueEqualityComparer : IEqualityComparer<Foo>
{
public bool Equals(Foo foo1, Foo foo2)
{
return foo1.Count == foo2.Count &&
foo1.Message == foo2.Message;
}
public int GetHashCode(Foo foo)
{
return foo.GetHashCode();
}
}
// Use it
public void MyTest()
{
// Arrange and Act
...
// Assert
Assert.IsTrue(new FooEqualityComparer().Equals(myFoo1, myFoo2));
}
Or, you could write a generic ValueEquals that works for all^* classes using Reflection:
public static class ValueEqualityComparer
{
public static bool ValueEquals<T>(this T self, T other) where T : class
{
var type = self.GetType();
if (type == typeof(string))
return self.Equals(other);
var properties = type.GetProperties(BindingFlags.Public | BindingFlags.Instance);
foreach (var property in properties)
{
var selfValue = property.GetValue(self);
var otherValue = property.GetValue(other);
// String is special, it's not primitive but is value equality like primitives
if (property.PropertyType.IsPrimitive || property.PropertyType == typeof(string))
{
if (!selfValue.Equals(otherValue))
return false;
}
// If the property is a List value equals each member
// Maybe find another type that allows indexing and is less restrictive
else if (typeof(IEnumerable).IsAssignableFrom(property.PropertyType))
{
var selfList = ((IEnumerable)property.GetValue(self)).Cast<object>();
var otherList = ((IEnumerable)property.GetValue(other)).Cast<object>();
try
{
// Using EquiZip from MoreLinq: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/EquiZip.cs
foreach (var element in selfList.EquiZip(otherList, (selfItem, otherItem) => new { selfItem, otherItem }))
{
if (!ValueEquals(element.selfItem, element.otherItem))
return false;
}
}
catch (InvalidOperationException)
{
// MoreLINQ throws a InvalidOperationException if our two enumerables aren't the same length
return false;
}
}
else
{
if (!ValueEquals(selfValue, otherValue))
return false;
}
}
return true;
}
}
This implementation is by no means perfect, and should honestly only be used for UnitTests and also should be thoroughly tested itself. You can see my tests as a dotnetfiddle here
Or you could do it "dirty" and serialize the objects to a string and compare those values.

Iterate through Nested Classes and Transform Property, Multiple Int by 2

The goal of this code is to iterate through multiple nested classes, and multiple any integer by 2. Provided simple example, however, example will be more complicated in future.
How do I change a Object to its underlying class? When I iterate through this function, it reads the type for OuterProduct correctly, but fails for InnerProduct reading as type System.RuntimeType, giving an error below
How can I resolve this code to multiply all nested integers by 2?
An unhandled exception of type 'System.StackOverflowException' occurred in Unknown Module.
class Program
{
static void Main(string[] args)
{
var test = new OuterProduct();
test.AmountSold = 5;
test.ProductName = "BookOuter";
test.InnerProduct = new InnerProduct();
test.InnerProduct.ProductNameInner = "BookInner";
test.InnerProduct.AmountSoldInner = 7;
ReadPropertiesTest.ReadPropertiesRecursive(test);
}
}
public class OuterProduct
{
public string ProductName { get; set; }
public int AmountSold { get; set; }
public InnerProduct InnerProduct { get; set; }
}
public class InnerProduct
{
public string ProductNameInner { get; set; }
public int AmountSoldInner { get; set; }
}
public static class ReadPropertiesTest
{
public static void ReadPropertiesRecursive(object test)
{
var type = test.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
if (property.PropertyType == typeof(int) || property.PropertyType == typeof(int?))
{
property.SetValue(test, (int)(property.GetValue(test)) * 2);
}
if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
{
ReadPropertiesRecursive(property.PropertyType);
}
}
}
}
Resources:
C#: How to get all public (both get and set) string properties of a type
How to iterate through nested properties of an object
System.RuntimeType is the implementation of the class that represents typeof(X) or something.GetType(). When you pass PropertyType to your function you are not passing the property value, but it's type.
You will need to pass the next object in the hierarchy into the recursive function by using GetValue.
Note though that this is dangerous and error prone. For example, if you have a List<> property you obviously cannot increase its Count (it is readonly!). You should check to make sure that the property can be written to using the CanWrite property.
You also need to check for null objects. On top of that we need to handle int differently from int? (otherwise casting null to int will throw). The latter we can clean up a bit with c#7 pattern matching:
public static void ReadPropertiesRecursive(object test)
{
if (test is null) // base case
return;
var type = test.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
// check if we can even read the property
if(!property.CanRead)
continue;
// use pattern matching on the value
// nulls will be ignored
// we *could* cache GetValue but then it means we will invoke it for uninteresting types/properties
// it's also why I don't call GetValue until we've inspected PropertyType
if (property.CanWrite &&
(property.PropertyType == typeof(int) || property.PropertyType == typeof(int?)) &&
property.GetValue(test) is int i)
{
property.SetValue(test, i * 2);
}
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
ReadPropertiesRecursive(property.GetValue(test));
}
}
}
An alternative version that omits some of the checks against PropertyType can also be used. It's a bit cleaner looking but it could potentially perform the GetValue reflection in cases where we don't need/want it (like on a double or a struct):
public static void ReadPropertiesRecursive(object test)
{
if (test is null) // base case
return;
var type = test.GetType();
foreach (PropertyInfo property in type.GetProperties())
{
// check if we can even read the property
if(!property.CanRead)
continue;
// possibly unnecessary if not int or class
var val = property.GetValue(test);
if (property.CanWrite && val is int i)
{
property.SetValue(test, i * 2);
}
else if (property.PropertyType.IsClass && property.PropertyType != typeof(string))
{
ReadPropertiesRecursive(val);
}
}
}
Note that you may want to have a whitelist or blacklist of types. Recursing into a Type object for example isn't going to get you much.
Alternative would be to go with more object-oriented approach. Make it responsibility of every class which need to be "updated".
For every type with properties which need to be updated introduce a method to do it.
public class OuterProduct
{
public string ProductName { get; set; }
public int AmountSold { get; set; }
public InnerProduct InnerProduct { get; set; }
public void Update()
{
AmountSold *= 2;
InnerProduct.Update();
}
}
public class InnerProduct
{
public string ProductNameInner { get; set; }
public int AmountSoldInner { get; set; }
public void Update()
{
AmountSoldInner *= 2;
}
}
// Usage is simple
var test = new OuterProduct
{
AmountSold = 5,
ProductName = "BookOuter",
InnerProduct = new InnerProduct
{
ProductNameInner = "BookInner",
AmountSoldInner = 7
}
};
test.Update();
// test.AmountSold == 10 is true
// test.InnerProduct.AmountSoldInner == 14 is true
This approach will simplify code maintenance. For example adding/removing properties or worse case scenario adding some other logic to Update method will be isolated in one class.
In your recursive call you are passing the type, not the actual property value:
if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
{
ReadPropertiesRecursive(property.PropertyType);
}
should be:
if (property.PropertyType.IsClass && !(property.PropertyType == typeof(string)))
{
ReadPropertiesRecursive(property.GetValue(test));
}

How to compare generic types based on constraints

Assume that I have two classes, Foo and Bar.
public class Foo<TFirst, TSecond, TThird, T>
where TFirst : IReadOnlyList<T>
where TThird : IEnumerable<T>
{
}
public class Bar<TFirst, TSecond, TThird, T>
where TFirst : IReadOnlyList<T>
{
}
Now I want to compare their generic types. I'm using equality comparer to operate on array of types. like Intersect, Subtract etc.
I don't want to compare Foo and Bar but I want to compare their Generic parameters.
for example if both type parameters have same constraint, they should be considered equal. if they have no constrained they should be considered equal too.
In above example TFirst of Foo should be considered equal to TFirst of Bar. as well as TSecond because they have no constraint. TThrids are not equal because they don't have same constraint.
So now I have Foo and Bar types. I want to analyze their type arguments and compare them against each other.
var fooType = typeof(Foo<,,,>);
var barType = typeof(Bar<,,,>);
var fooArgs = fooType.GetGenericArguments();
var barArgs = barType.GetGenericArguments();
var commonArgs = fooArgs.Intersect(barArgs, new GenericArgumentEqualityComparer()).ToArray();
var unknownBarArgs = barArgs.Except(commonArgs, new GenericArgumentEqualityComparer()).ToArray();
Following Equality comparer always returns false, no matter I use IsAssignableFrom or ==. what is the right way to do this?
public class GenericArgumentEqualityComparer : IEqualityComparer<Type>
{
public bool Equals(Type x, Type y)
{
if (x == null || y == null) return false;
var xcons = x.GetGenericParameterConstraints();
var ycons = y.GetGenericParameterConstraints();
if(xcons.Length != ycons.Length) return false;
foreach (var cons in xcons)
{
if (ycons.All(cons2 => !cons.IsAssignableFrom(cons2)))
return false;
}
return true;
}
public int GetHashCode(Type obj)
{
// code runs on T4 for code generation. performance doesn't matter.
return 0;
}
}
the unexpected behavior comes from the
cons.IsAssignableFrom
you cannot try to assign open generic type from derived to base even if hierarchically possible
take this as an example
var ien = typeof(IEnumerable<string>);
var iread = typeof(IReadOnlyList<string>);
//isAssignable will be true
var isAssignable = ien.IsAssignableFrom(iread);
//here because IsAssignableFrom work from BaseType.IsAssignableFrom(DerviedType)
var isAssignableIEn = iread.IsAssignableFrom(ien);
and this One
var ien = typeof(IEnumerable<>);
var iread = typeof(IReadOnlyList<>);
var isAssignable = ien.IsAssignableFrom(iread);
//here because IsAssignableFrom work from BaseType.IsAssignableFrom(DerviedType)
var isAssignableIEn = iread.IsAssignableFrom(ien);
both of the assignment checks will be false this is an expected behavior because an open generic type cannot be instantiated by default so is not assignable
to create an instance of open generic type you should use Type.MakeGenericType
To resolve your issue this may help you
public class GenericArgumentEqualityComparer : IEqualityComparer<Type>
{
public bool Equals(Type x, Type y)
{
var xInterfacesTypes = x.GetInterfaces();
var yInterfacesTypes = y.GetInterfaces();
if (!xInterfacesTypes.Any()&&!yInterfacesTypes.Any() )
{
return true;
}
if ((!xInterfacesTypes.Any() && yInterfacesTypes.Any()) || xInterfacesTypes.Any() && !yInterfacesTypes.Any())
{
return false;
}
foreach (var xInterfacesType in xInterfacesTypes)
{
var iType = xInterfacesType.IsGenericType ? xInterfacesType.GetGenericTypeDefinition() :xInterfacesType;
var yType = yInterfacesTypes.Any(yI => yI.IsGenericType && yI.GetGenericTypeDefinition() == iType||yI.GetType()==xInterfacesType.GetType());
if (!yType)
{
return false;
}
}
return true;
}
public int GetHashCode(Type obj)
{
return obj.Name.GetHashCode();
}
}

Excluding an aspect from specific implementations of a generic class?

Say I have a generic class:
public abstract class MyClass<T> {
// Contents
}
How can I specify an exclude for my aspect for only certain types of T? I am adding aspects in ithe AssemblyInfo.cs, like so:
[assembly: LogMethod(AttributePriority = 0,
AttributeTargetTypeAttributes = MulticastAttributes.Public,
AttributeTargetMemberAttributes = MulticastAttributes.Public,
AttributeTargetElements = MulticastTargets.Method)]
It is not possible to apply generic arguments filtering in a declarative way. For advanced filtering of the aspect targets you can override CompileTimeValidate method on your aspect and filter programmatically.
However, even this is not going to be enough in the case you've described. Let's say you've applied the aspect to a method in MyClass<T>. At this point in compilation the T is not known yet, so it's not possible to perform the check. The concrete T is known when a field or variable of MyClass<T> is declared somewhere else in the code.
The best option I can see in your case is to make the aspect instance-scoped and validate at run-time for each instance of the target class. You can find the sample implementation of this approach below.
[PSerializable]
public class LogMethodAttribute : OnMethodBoundaryAspect, IInstanceScopedAspect
{
private bool disabled;
public override void OnEntry( MethodExecutionArgs args )
{
if ( !this.disabled )
{
Console.WriteLine( "OnEntry: {0}({1})", args.Method.Name, args.Arguments.GetArgument( 0 ) );
}
}
public object CreateInstance( AdviceArgs adviceArgs )
{
LogMethodAttribute clone = (LogMethodAttribute) this.MemberwiseClone();
Type type = adviceArgs.Instance.GetType();
if ( type.IsGenericType )
{
Type[] genericArguments = type.GetGenericArguments();
// Filter out targets where T is string.
if ( genericArguments[0] == typeof( string ) )
{
clone.disabled = true;
}
}
return clone;
}
public void RuntimeInitializeInstance()
{
}
}
class Program
{
static void Main( string[] args )
{
var obj1 = new Class1<int>();
obj1.Method1(1);
var obj2 = new Class1<string>();
obj2.Method1("a");
}
}
[LogMethod(AttributeTargetElements = MulticastTargets.Method)]
public class Class1<T>
{
public void Method1(T a)
{
}
}

Custom business object comparer

I need to implement mechanism that compares two business objects and return the list of differences (past value, new value, isDifferenceBetter).
Because not all fields of class has to be compared and one fields need to be compared with different function then the other (sometimes < is better sometimes > is better ... ) I figured out that I need to implelemnt custom attribute and give it to each field that has to be compared in this object.
This attribute must have:
- name
- delegate or sth to point to the function which would be applied for comparision (dont know how to do it so far)
So could anyone suggest me if its a good idea? Maybe any other ideas.
Using attributes I would be able to use refflection to iterate through each field with attribute and invoke needed delegate.
thanks for help
bye
See my example below.
May be, it can help you:
namespace ConsoleApplication5
{
class FunctionToCompareAttribute : Attribute
{
public FunctionToCompareAttribute( String className, String methodName )
{
ClassName = className;
MethodName = methodName;
}
public String ClassName
{
get;
private set;
}
public String MethodName
{
get;
private set;
}
}
class ComparableAttribute : Attribute
{
}
class CompareResult
{
}
[Comparable]
class ClassToCompare
{
[FunctionToCompare( "ConsoleApplication5.ClassToCompare", "MyCompareFunction" )]
public String SomeProperty
{
get;
private set;
}
public static CompareResult MyCompareFunction( Object left, Object right, String propertyName )
{
return null;//Comparsion
}
}
class Program
{
static void Main( string[] args )
{
var left = new ClassToCompare();
var right = new ClassToCompare();
var type = typeof( ClassToCompare );
var typeAttributes = type.GetCustomAttributes( typeof( ComparableAttribute ), true );
if ( typeAttributes.Length == 0 )
return;
foreach ( var property in type.GetProperties() )
{
var attributes = property.GetCustomAttributes( typeof( FunctionToCompareAttribute ), true );
if ( attributes.Length == 0 )
continue;
var compareAttribute = attributes[ 0 ] as FunctionToCompareAttribute;
var className = compareAttribute.ClassName;
var methodName = compareAttribute.MethodName;
var compareType = Type.GetType( className );
var method = compareType.GetMethod( methodName, new Type[] { type, type, typeof( String ) } );
var **result** = method.Invoke( null, new Object[] { left, right, property.Name } ) as CompareResult;
}
}
}
}
Do some search about self tracking objects and the way ORMs(like NHibernate) checking an object for dirty fields
Certainly possible but perhaps you should be thinking along more abstract terms. Maybe a pair of attributes [LowerValueIsBetter] and [HigherValueIsBetter] would enable you to express this information in a more cohesive way.

Categories

Resources