Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions Assets/Example/Example_Generics.cs
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,32 @@ public void DoAction (IActor actor)
public IActor Actor => null;
}

namespace TestExample.Generics
{
public interface IObjectHolder<T>
{
T Value { get; }
}

[Serializable]
public sealed class ObjectHolder<T> : IObjectHolder<T>
{
[SerializeField]
private T value;

public T Value => value;
}

[Serializable]
public sealed class ParticleSystemHolder : IObjectHolder<ParticleSystem>
{
[SerializeField]
private ParticleSystem value;

public ParticleSystem Value => value;
}
}

public class Example_Generics : MonoBehaviour
{

Expand All @@ -98,4 +124,10 @@ public class Example_Generics : MonoBehaviour
[SerializeReference, SubclassSelector]
public List<ICovarianceAction<INetworkActor>> covarianceActions = new List<ICovarianceAction<INetworkActor>>();

[SerializeReference, SubclassSelector]
public TestExample.Generics.IObjectHolder<GameObject> gameObjectHolder = null;

[SerializeReference, SubclassSelector]
public TestExample.Generics.IObjectHolder<ParticleSystem> particleSystemHolder = null;

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ public static string[] GetSplittedTypePath (Type type)
}
else
{
int splitIndex = type.FullName.LastIndexOf('.');
// In the case of Generic, type information is included as shown below, so it must be extracted.
// TestNamespace.TestClass`1[[UnityEngine.GameObject, UnityEngine.CoreModule, Version=0.0.0.0, Culture=neutral, PublicKeyToken=null]]
var name = type.IsGenericType ? type.FullName.Substring(0, type.FullName.IndexOf("[")) : type.FullName;

int splitIndex = name.LastIndexOf('.');
if (splitIndex >= 0)
{
return new string[] { type.FullName.Substring(0, splitIndex), type.FullName.Substring(splitIndex + 1) };
return new string[] { name.Substring(0, splitIndex), name.Substring(splitIndex + 1) };
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,6 @@ namespace MackySoft.SerializeReferenceExtensions.Editor
{
public interface IIntrinsicTypePolicy
{
bool IsAllowed (Type candiateType);
bool IsAllowed (Type candiateType, bool ignoreGenericTypeCheck);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using UnityEngine;

namespace MackySoft.SerializeReferenceExtensions.Editor
{
Expand All @@ -7,12 +8,12 @@ public sealed class DefaultIntrinsicTypePolicy : IIntrinsicTypePolicy

public static readonly DefaultIntrinsicTypePolicy Instance = new DefaultIntrinsicTypePolicy();

public bool IsAllowed (Type candiateType)
public bool IsAllowed (Type candiateType, bool ignoreGenericTypeCheck)
{
return
(candiateType.IsPublic || candiateType.IsNestedPublic || candiateType.IsNestedPrivate) &&
!candiateType.IsAbstract &&
!candiateType.IsGenericType &&
(ignoreGenericTypeCheck || !candiateType.IsGenericType) &&
!candiateType.IsPrimitive &&
!candiateType.IsEnum &&
!typeof(UnityEngine.Object).IsAssignableFrom(candiateType) &&
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public IEnumerable<Type> GetTypeCandidates (Type baseType)
{
return TypeCache.GetTypesDerivedFrom(baseType)
.Append(baseType)
.Where(intrinsicTypePolicy.IsAllowed);
.Where(x => intrinsicTypePolicy.IsAllowed(x, false));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;

namespace MackySoft.SerializeReferenceExtensions.Editor
{
Expand Down Expand Up @@ -46,23 +47,29 @@ private IEnumerable<Type> GetTypesWithGeneric (Type baseType)

result = new List<Type>();

IEnumerable<Type> types = EnumerateAllTypesSafely();
bool isGenericBaseType = baseType.IsGenericType;
Type genericTypeDefinition = isGenericBaseType ? baseType.GetGenericTypeDefinition() : baseType;
Type[] targetTypeArguments = isGenericBaseType ? baseType.GetGenericArguments() : Type.EmptyTypes;
IEnumerable<Type> types = TypeCache.GetTypesDerivedFrom(genericTypeDefinition);
foreach (Type type in types)
{
if (!intrinsicTypePolicy.IsAllowed(type))
// If the type is Generic, create a MakeGenericType from the Arguments of the baseType.
Type targetType = type.IsGenericType ? type.MakeGenericType(targetTypeArguments) : type;

if (!intrinsicTypePolicy.IsAllowed(targetType, targetType != type))
{
continue;
}
if (!typeCompatibilityPolicy.IsCompatible(baseType, type))
if (!typeCompatibilityPolicy.IsCompatible(baseType, targetType))
{
continue;
}

result.Add(type);
result.Add(targetType);
}

// Include the base type itself if allowed
if (intrinsicTypePolicy.IsAllowed(baseType) && typeCompatibilityPolicy.IsCompatible(baseType, baseType))
if (intrinsicTypePolicy.IsAllowed(baseType, false) && typeCompatibilityPolicy.IsCompatible(baseType, baseType))
{
result.Add(baseType);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,16 +8,12 @@ public sealed class TypeCandiateService
{

private readonly ITypeCandiateProvider typeCandiateProvider;
private readonly IIntrinsicTypePolicy intrinsicTypePolicy;
private readonly ITypeCompatibilityPolicy typeCompatibilityPolicy;

private readonly Dictionary<Type, Type[]> typeCache = new Dictionary<Type, Type[]>();

public TypeCandiateService (ITypeCandiateProvider typeCandiateProvider, IIntrinsicTypePolicy intrinsicTypePolicy, ITypeCompatibilityPolicy typeCompatibilityPolicy)
public TypeCandiateService (ITypeCandiateProvider typeCandiateProvider)
{
this.typeCandiateProvider = typeCandiateProvider ?? throw new ArgumentNullException(nameof(typeCandiateProvider));
this.intrinsicTypePolicy = intrinsicTypePolicy ?? throw new ArgumentNullException(nameof(intrinsicTypePolicy));
this.typeCompatibilityPolicy = typeCompatibilityPolicy ?? throw new ArgumentNullException(nameof(typeCompatibilityPolicy));
}

public IReadOnlyList<Type> GetDisplayableTypes (Type baseType)
Expand All @@ -33,8 +29,6 @@ public IReadOnlyList<Type> GetDisplayableTypes (Type baseType)

var candiateTypes = typeCandiateProvider.GetTypeCandidates(baseType);
var result = candiateTypes
.Where(intrinsicTypePolicy.IsAllowed)
.Where(t => typeCompatibilityPolicy.IsCompatible(baseType, t))
.Distinct()
.ToArray();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,24 +3,18 @@
public static class TypeSearchService
{

public static readonly IIntrinsicTypePolicy IntrinsicTypePolicy;
public static readonly ITypeCompatibilityPolicy TypeCompatibilityPolicy;
public static readonly ITypeCandiateProvider TypeCandiateProvider;
public static readonly TypeCandiateService TypeCandiateService;

static TypeSearchService ()
{
IntrinsicTypePolicy = DefaultIntrinsicTypePolicy.Instance;

#if UNITY_2023_2_OR_NEWER
TypeCompatibilityPolicy = Unity_2023_2_OrNewer_GenericVarianceTypeCompatibilityPolicy.Instance;
TypeCandiateProvider = Unity_2023_2_OrNewer_TypeCandiateProvider.Instance;
#else
TypeCompatibilityPolicy = DefaultTypeCompatibilityPolicy.Instance;
TypeCandiateProvider = DefaultTypeCandiateProvider.Instance;
#endif

TypeCandiateService = new TypeCandiateService(TypeCandiateProvider, IntrinsicTypePolicy, TypeCompatibilityPolicy);
TypeCandiateService = new TypeCandiateService(TypeCandiateProvider);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public static IEnumerable<TestCaseData> Cases ()
[TestCaseSource(nameof(Cases))]
public void IsAllowed_MatchesExpected (Type type, bool expected)
{
bool actual = DefaultIntrinsicTypePolicy.Instance.IsAllowed(type);
bool actual = DefaultIntrinsicTypePolicy.Instance.IsAllowed(type, false);
Assert.That(actual, Is.EqualTo(expected), type.FullName);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Linq;
using MackySoft.SerializeReferenceExtensions.Editor;
using NUnit.Framework;
using UnityEngine;

namespace MackySoft.SerializeReferenceExtensions.Tests
{
Expand Down Expand Up @@ -39,6 +40,33 @@ public void Invariance_RemainsStrict ()
Assert.That(set, Does.Contain(typeof(Invariant_Actor)));
Assert.That(set, !Does.Contain(typeof(Invariant_NetworkActor)));
}

[Test]
public void GenericClass_ParticleSystem_IsSupported ()
{
var set = TypeSearchService.TypeCandiateService.GetDisplayableTypes(typeof(IObjectHolder<ParticleSystem>)).ToHashSet();

Assert.That(set, Does.Contain(typeof(ObjectHolder<ParticleSystem>)));
Assert.That(set, Does.Contain(typeof(ParticleSystemHolder)));
}

[Test]
public void GenericClass_GameObject_IsSupported ()
{
var set = TypeSearchService.TypeCandiateService.GetDisplayableTypes(typeof(IObjectHolder<GameObject>)).ToHashSet();

Assert.That(set, Does.Contain(typeof(ObjectHolder<GameObject>)));
Assert.That(set, !Does.Contain(typeof(ParticleSystemHolder)));
}

[Test]
public void GenericClass_Integer_IsSupported ()
{
var set = TypeSearchService.TypeCandiateService.GetDisplayableTypes(typeof(IObjectHolder<int>)).ToHashSet();

Assert.That(set, Does.Contain(typeof(ObjectHolder<int>)));
Assert.That(set, !Does.Contain(typeof(ParticleSystemHolder)));
}
}
}
#endif
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@ public interface IContravariant<in T> { }
public interface ICovariant<out T> { T Create (); }
public interface IInvariant<T> { }

public interface IObjectHolder<T> { }

[Serializable]
public sealed class Contravariant_Actor : IContravariant<IActor> { }

Expand Down Expand Up @@ -93,4 +95,10 @@ public sealed class Invariant_Actor : IInvariant<IActor> { }

[Serializable]
public sealed class Invariant_NetworkActor : IInvariant<INetworkActor> { }

[Serializable]
public sealed class ObjectHolder<T> : IObjectHolder<T> { }

[Serializable]
public sealed class ParticleSystemHolder : IObjectHolder<ParticleSystem> { }
}
Loading