public void RegisterSubclass(Type type) { var typeInfo = type.GetTypeInfo(); if (!typeof(ParseObject).GetTypeInfo().IsAssignableFrom(typeInfo)) { throw new ArgumentException("Cannot register a type that is not a subclass of ParseObject"); } var className = GetClassName(type); try { // Perform this as a single independent transaction, so we can never get into an // intermediate state where we *theoretically* register the wrong class due to a // TOCTTOU bug. _mutex.EnterWriteLock(); if (_registeredSubclasses.TryGetValue(className, out var previousInfo)) { if (typeInfo.IsAssignableFrom(previousInfo.TypeInfo)) { // Previous subclass is more specific or equal to the current type, do nothing. return; } else if (previousInfo.TypeInfo.IsAssignableFrom(typeInfo)) { // Previous subclass is parent of new child, fallthrough and actually register // this class. /* Do nothing */ } else { throw new ArgumentException( "Tried to register both " + previousInfo.TypeInfo.FullName + " and " + typeInfo.FullName + " as the ParseObject subclass of " + className + ". Cannot determine the right class " + "to use because neither inherits from the other." ); } } ConstructorInfo constructor = type.FindConstructor(); if (constructor == null) { throw new ArgumentException( "Cannot register a type that does not implement the default constructor!"); } _registeredSubclasses[className] = new ObjectSubclassInfo(type, constructor); } finally { _mutex.ExitWriteLock(); } _mutex.EnterReadLock(); _registerActions.TryGetValue(className, out var toPerform); _mutex.ExitReadLock(); toPerform?.Invoke(); }
public string GetClassName(Type type) { return(type == typeof(ParseObject) ? ParseObjectClassName : ObjectSubclassInfo.GetClassName(type.GetTypeInfo())); }