Skip to content

Commit

Permalink
feat(extensions): add GetGenericParentType method to Type extensions
Browse files Browse the repository at this point in the history
  • Loading branch information
jayharris committed Dec 1, 2022
1 parent 9fd149a commit 37991f4
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 8 deletions.
1 change: 1 addition & 0 deletions src/cobweb/Cobweb.sln.DotSettings
Expand Up @@ -67,6 +67,7 @@
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=VCS/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=VsBulb/@EntryIndexedValue">DO_NOTHING</s:String>
<s:String x:Key="/Default/Environment/PerformanceGuide/SwitchBehaviour/=XAML_0020Designer/@EntryIndexedValue">LIVE_MONITOR</s:String>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EdotCover_002EIde_002ECore_002EFilterManagement_002EModel_002ESolutionFilterSettingsManagerMigrateSettings/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpAttributeForSingleLineMethodUpgrade/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
Expand Down
42 changes: 42 additions & 0 deletions src/cobweb/src/cobweb/Extentions/WithType.cs
Expand Up @@ -72,5 +72,47 @@ public static class WithType {
public static bool CanBeNull(this Type type) {
return !type.IsValueType || (Nullable.GetUnderlyingType(type) != null);
}

/// <summary>
/// Returns the parent type definition of a matching generic type
/// </summary>
/// <param name="currentType">The type to analyze</param>
/// <param name="genericBaseType">The generic parent type</param>
/// <returns>The matching parent type definition</returns>
/// <exception cref="ArgumentNullException"></exception>
/// <exception cref="ArgumentException"></exception>
/// <exception cref="Exception"></exception>
public static Type GetGenericParentType(this Type currentType, Type genericBaseType) {
while (true) {
if (genericBaseType == null) {
throw new ArgumentNullException(nameof(genericBaseType));
}

if (!genericBaseType.IsGenericType) {
throw new ArgumentException("Type must be generic", nameof(genericBaseType));
}

if (!currentType.IsAssignableToGeneric(genericBaseType)) {
throw new ArgumentException($"Type {currentType.FullName} is not assignable to {genericBaseType.FullName}", nameof(genericBaseType));
}

if (currentType.IsGenericType && currentType.GetGenericTypeDefinition() == genericBaseType) {
return currentType;
}

if (!genericBaseType.IsInterface) {
currentType = currentType.BaseType ?? throw new Exception("Unable to find matching parent type definition");
continue;
}

foreach (var @interface in currentType.GetInterfaces()) {
var matchingInterface = GetGenericParentType(@interface, genericBaseType);

if (matchingInterface != null) return matchingInterface;
}

throw new Exception("Unable to find matching parent type definition");
}
}
}
}
2 changes: 1 addition & 1 deletion src/cobweb/test/cobweb.tests/Aranasoft.Cobweb.Tests.csproj
Expand Up @@ -2,7 +2,7 @@
<Import Project="..\..\..\..\netfx.props" />

<PropertyGroup>
<TargetFrameworks>net452;netcoreapp2.1</TargetFrameworks>
<TargetFrameworks>net452;netcoreapp2.1;netcoreapp3.1;net6.0</TargetFrameworks>
<AssemblyName>Aranasoft.Cobweb.Tests</AssemblyName>
<RootNamespace>Aranasoft.Cobweb.Tests</RootNamespace>
</PropertyGroup>
Expand Down
@@ -1,4 +1,5 @@
using Aranasoft.Cobweb.Extentions;
using System;
using Aranasoft.Cobweb.Extentions;
using FluentAssertions;
using Xunit;

Expand All @@ -11,24 +12,76 @@ public class Animal<T> where T : Animal<T> {}
typeof(object).IsAssignableToGeneric(typeof(Animal<>)).Should().BeFalse();
}


public class GivenADerivedTypeThatIsNonGeneric {
public class GivenAChildTypeThatIsNonGeneric {
public class Fish : Animal<Fish> {}

[Fact]
public void ItShouldIdentifyTheDerivedTypeAsDerived() {
public void ItShouldIdentifyTheParentTypeAsDerived() {
typeof(Fish).IsAssignableToGeneric(typeof(Animal<>)).Should().BeTrue();
}

[Fact]
public void ItShouldIdentifyAnUnrelatedTypeAsNotDerived() {
typeof(Fish).IsAssignableToGeneric(typeof(Nullable<>)).Should().BeFalse();
}

[Fact]
public void ItShouldIdentifyTheGenericParentTypeDefinition() {
typeof(Fish).GetGenericParentType(typeof(Animal<>)).Should().Be(typeof(Animal<Fish>));
}
}


public class GivenADerivedTypeThatIsGeneric {
public class GivenAChildTypeThatIsGeneric {
public class FlyingAnimal<T> : Animal<FlyingAnimal<T>> {}

[Fact]
public void ItShouldIdentifyTheDerivedTypeAsDerived() {
public void ItShouldIdentifyTheParentTypeAsDerived() {
typeof(FlyingAnimal<>).IsAssignableToGeneric(typeof(Animal<>)).Should().BeTrue();
}

[Fact]
public void ItShouldIdentifyAnUnrelatedTypeAsNotDerived() {
typeof(FlyingAnimal<>).IsAssignableToGeneric(typeof(Nullable<>)).Should().BeFalse();
}

[Fact]
public void ItShouldIdentifyTheGenericParentTypeDefinition() {
typeof(FlyingAnimal<object>).GetGenericParentType(typeof(Animal<>)).Should().Be(typeof(Animal<FlyingAnimal<object>>));
}

[Fact]
public void ItShouldIdentifyTheGenericSelfTypeDefinition() {
typeof(FlyingAnimal<object>).GetGenericParentType(typeof(FlyingAnimal<>)).Should().Be(typeof(FlyingAnimal<object>));
}

public class GivenAGrandchildTypeThatIsNonGeneric {
public class Bird : FlyingAnimal<Bird> {}

[Fact]
public void ItShouldIdentifyTheParentTypeAsDerived() {
typeof(Bird).IsAssignableToGeneric(typeof(FlyingAnimal<>)).Should().BeTrue();
}

[Fact]
public void ItShouldIdentifyTheGrandparentTypeAsDerived() {
typeof(Bird).IsAssignableToGeneric(typeof(Animal<>)).Should().BeTrue();
}

[Fact]
public void ItShouldIdentifyAnUnrelatedTypeAsNotDerived() {
typeof(Bird).IsAssignableToGeneric(typeof(Nullable<>)).Should().BeFalse();
}

[Fact]
public void ItShouldIdentifyTheGenericGrandparentTypeDefinition() {
typeof(Bird).GetGenericParentType(typeof(Animal<>)).Should().Be(typeof(Animal<FlyingAnimal<Bird>>));
}

[Fact]
public void ItShouldIdentifyTheGenericParentTypeDefinition() {
typeof(Bird).GetGenericParentType(typeof(FlyingAnimal<>)).Should().Be(typeof(FlyingAnimal<Bird>));
}
}
}
}
}

0 comments on commit 37991f4

Please sign in to comment.