Jelajahi Sumber

C# properties can now be inspected and serialized
Added ShowInInspector attribute to force fields/properties to be displayed in the inspector

BearishSun 9 tahun lalu
induk
melakukan
7292bed6b2

+ 13 - 3
Source/BansheeMono/Include/BsMonoClass.h

@@ -72,9 +72,9 @@ namespace BansheeEngine
 		 *
 		 * @note
 		 * Does not query base class properties.
-		 * Throws an exception if the property cannot be found.
+		 * Returns null if property cannot be found.
 		 */
-		MonoProperty& getProperty(const String& name);
+		MonoProperty* getProperty(const String& name) const;
 
 		/**
 		 * Returns an instance of an attribute of the specified @p monoClass that is part of this class. Returns null if
@@ -102,6 +102,13 @@ namespace BansheeEngine
 		 */
 		const Vector<MonoField*>& getAllFields() const;
 
+		/**
+		 * Returns all properties belonging to this class.
+		 *
+		 * @note	Be aware this will not include the properties of any base classes.
+		 */
+		const Vector<MonoProperty*>& getAllProperties() const;
+
 		/**
 		 * Returns all methods belonging to this class.
 		 *
@@ -203,11 +210,14 @@ namespace BansheeEngine
 
 		mutable UnorderedMap<MethodId, MonoMethod*, MethodId::Hash, MethodId::Equals> mMethods; 
 		mutable UnorderedMap<String, MonoField*> mFields; 
-		UnorderedMap<String, MonoProperty*> mProperties;
+		mutable UnorderedMap<String, MonoProperty*> mProperties;
 
 		mutable bool mHasCachedFields;
 		mutable Vector<MonoField*> mCachedFieldList;
 
+		mutable bool mHasCachedProperties;
+		mutable Vector<MonoProperty*> mCachedPropertyList;
+
 		mutable bool mHasCachedMethods;
 		mutable Vector<MonoMethod*> mCachedMethodList;
 	};

+ 13 - 0
Source/BansheeMono/Include/BsMonoProperty.h

@@ -17,6 +17,9 @@ namespace BansheeEngine
 	class BS_MONO_EXPORT MonoProperty
 	{
 	public:
+		/**	Returns the name of the property. */
+		const String& getName() const { return mName; }
+
 		/**
 		 * Returns a boxed value contained in the property in the specified instance.
 		 *
@@ -58,6 +61,15 @@ namespace BansheeEngine
 
 		/**	Returns the data type the property holds. */
 		MonoClass* getReturnType() const;
+
+		/**	Checks if property has an attribute of the specified type. */
+		bool hasAttribute(MonoClass* monoClass);
+
+		/**
+		 * Returns an instance of an attribute of the specified type. Returns null if the property doesn't have such an
+		 * attribute.
+		 */
+		MonoObject* getAttribute(MonoClass* monoClass);
 	private:
 		friend class MonoClass;
 
@@ -69,6 +81,7 @@ namespace BansheeEngine
 		 */
 		void initializeDeferred() const;
 
+		String mName;
 		::MonoProperty* mProperty;
 		::MonoMethod* mGetMethod;
 		::MonoMethod* mSetMethod;

+ 29 - 9
Source/BansheeMono/Source/BsMonoClass.cpp

@@ -35,7 +35,7 @@ namespace BansheeEngine
 
 	MonoClass::MonoClass(const String& ns, const String& type, ::MonoClass* monoClass, const MonoAssembly* parentAssembly)
 		: mParentAssembly(parentAssembly), mClass(monoClass), mNamespace(ns), mTypeName(type), mHasCachedFields(false)
-		, mHasCachedMethods(false)
+		, mHasCachedProperties(false), mHasCachedMethods(false)
 	{
 		mFullName = ns + "." + type;
 	}
@@ -142,23 +142,20 @@ namespace BansheeEngine
 		return newField;
 	}
 
-	MonoProperty& MonoClass::getProperty(const String& name)
+	MonoProperty* MonoClass::getProperty(const String& name) const
 	{
 		auto iterFind = mProperties.find(name);
 		if(iterFind != mProperties.end())
-			return *iterFind->second;
+			return iterFind->second;
 
 		::MonoProperty* property = mono_class_get_property_from_name(mClass, name.c_str());
-		if(property == nullptr)
-		{
-			String fullPropertyName = mFullName + "." + name;
-			BS_EXCEPT(InvalidParametersException, "Cannot get Mono property: " + fullPropertyName);
-		}
+		if (property == nullptr)
+			return nullptr;
 
 		MonoProperty* newProperty = new (bs_alloc<MonoProperty>()) MonoProperty(property);
 		mProperties[name] = newProperty;
 
-		return *newProperty;
+		return newProperty;
 	}
 
 	const Vector<MonoField*>& MonoClass::getAllFields() const
@@ -184,6 +181,29 @@ namespace BansheeEngine
 		return mCachedFieldList;
 	}
 
+	const Vector<MonoProperty*>& MonoClass::getAllProperties() const
+	{
+		if (mHasCachedProperties)
+			return mCachedPropertyList;
+
+		mCachedPropertyList.clear();
+
+		void* iter = nullptr;
+		::MonoProperty* curClassProperty = mono_class_get_properties(mClass, &iter);
+		while (curClassProperty != nullptr)
+		{
+			const char* propertyName = mono_property_get_name(curClassProperty);
+			MonoProperty* curProperty = getProperty(propertyName);
+
+			mCachedPropertyList.push_back(curProperty);
+
+			curClassProperty = mono_class_get_properties(mClass, &iter);
+		}
+
+		mHasCachedProperties = true;
+		return mCachedPropertyList;
+	}
+
 	const Vector<MonoMethod*>& MonoClass::getAllMethods() const
 	{
 		if (mHasCachedMethods)

+ 35 - 0
Source/BansheeMono/Source/BsMonoProperty.cpp

@@ -3,6 +3,7 @@
 #include "BsMonoProperty.h"
 #include "BsMonoMethod.h"
 #include "BsMonoManager.h"
+#include "BsMonoClass.h"
 #include <mono/jit/jit.h>
 
 namespace BansheeEngine
@@ -12,6 +13,8 @@ namespace BansheeEngine
 	{
 		mGetMethod = mono_property_get_get_method(mProperty);
 		mSetMethod = mono_property_get_set_method(mProperty);
+
+		mName = mono_property_get_name(mProperty);
 	}
 
 	MonoObject* MonoProperty::get(MonoObject* instance) const
@@ -63,6 +66,38 @@ namespace BansheeEngine
 		return mGetReturnType;
 	}
 
+	bool MonoProperty::hasAttribute(MonoClass* monoClass)
+	{
+		// TODO - Consider caching custom attributes or just initializing them all at load
+
+		::MonoClass* parentClass = mono_property_get_parent(mProperty);
+		MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_property(parentClass, mProperty);
+		if (attrInfo == nullptr)
+			return false;
+
+		bool hasAttr = mono_custom_attrs_has_attr(attrInfo, monoClass->_getInternalClass()) != 0;
+
+		mono_custom_attrs_free(attrInfo);
+
+		return hasAttr;
+	}
+
+	MonoObject* MonoProperty::getAttribute(MonoClass* monoClass)
+	{
+		// TODO - Consider caching custom attributes or just initializing them all at load
+
+		::MonoClass* parentClass = mono_property_get_parent(mProperty);
+		MonoCustomAttrInfo* attrInfo = mono_custom_attrs_from_property(parentClass, mProperty);
+		if (attrInfo == nullptr)
+			return nullptr;
+
+		MonoObject* foundAttr = mono_custom_attrs_get_attr(attrInfo, monoClass->_getInternalClass());
+
+		mono_custom_attrs_free(attrInfo);
+
+		return foundAttr;
+	}
+
 	void MonoProperty::initializeDeferred() const
 	{
 		if (mGetMethod != nullptr)

+ 1 - 0
Source/MBansheeEngine/MBansheeEngine.csproj

@@ -55,6 +55,7 @@
     <Compile Include="Audio\Interop\NativeAudioSource.cs" />
     <Compile Include="GUI\GUICanvas.cs" />
     <Compile Include="Rendering\PostProcessSettings.cs" />
+    <Compile Include="Serialization\ShowInInspector.cs" />
     <Compile Include="Serialization\Step.cs" />
     <Compile Include="Utility\AsyncOp.cs" />
     <Compile Include="Math\Bounds.cs" />

+ 3 - 3
Source/MBansheeEngine/Serialization/HideInInspector.cs

@@ -9,11 +9,11 @@ namespace BansheeEngine
      */
 
     /// <summary>
-    /// Attribute that prevents a field from being visible in the inspector window in editor. Normally those fields
-    /// are public fields of a <see cref="Component"/>, <see cref="Resource"/> or a class marked with a 
+    /// Attribute that prevents a field or a property from being visible in the inspector window in editor. Normally those
+    /// fields are public fields of a <see cref="Component"/>, <see cref="Resource"/> or a class marked with a 
     /// <see cref="SerializeObject"/> attribute.
     /// </summary>
-    [AttributeUsage(AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
     public sealed class HideInInspector : Attribute
     {
     }

+ 5 - 4
Source/MBansheeEngine/Serialization/SerializeField.cs

@@ -9,11 +9,12 @@ namespace BansheeEngine
      */
 
     /// <summary>
-    /// Attribute that may be used on fields to mark them as serializable. Normally public fields are serialized
-    /// by default, and you can use this attribute to mark private/protected/internal fields as serialized as well.
-    /// Fields must be in a serializable object otherwise this attribute has no effect.
+    /// Attribute that may be used on fields or properties to mark them as serializable. Normally public fields are 
+    /// serialized by default, and you can use this attribute to mark private/protected/internal fields as serialized, or to
+    /// mark properties (never serialized by default) as serializable as well. Member must be in a serializable object
+    /// otherwise this attribute has no effect, and the type of the field/property must be a serializable type.
     /// </summary>
-    [AttributeUsage(AttributeTargets.Field)]
+    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
     public sealed class SerializeField : Attribute
     {
     }

+ 23 - 0
Source/MBansheeEngine/Serialization/ShowInInspector.cs

@@ -0,0 +1,23 @@
+//********************************** Banshee Engine (www.banshee3d.com) **************************************************//
+//**************** Copyright (c) 2016 Marko Pintera ([email protected]). All rights reserved. **********************//
+using System;
+
+namespace BansheeEngine
+{
+    /** @addtogroup Serialization
+     *  @{
+     */
+
+    /// <summary>
+    /// Attribute that forces a field or a property to be visible in the inspector window in editor. Normally public fields
+    /// are show in the inspector by default, and you can use this attribute to show private/protected/internal fields, or
+    /// to show properties (never shown by default) as well. Member must be in a serializable object otherwise this
+    /// attribute has no effect, and the type of the field/property must be a serializable type.
+    /// </summary>
+    [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property)]
+    public sealed class ShowInInspector : Attribute
+    {
+    }
+
+    /** @} */
+}

+ 35 - 0
Source/SBansheeEngine/Include/BsManagedSerializableObjectInfo.h

@@ -338,6 +338,41 @@ namespace BansheeEngine
 		RTTITypeBase* getRTTI() const override;
 	};
 
+	/**	Contains data about a single property in a managed complex object. */
+	class BS_SCR_BE_EXPORT ManagedSerializablePropertyInfo : public ManagedSerializableMemberInfo
+	{
+	public:
+		ManagedSerializablePropertyInfo();
+
+		/** @copydoc ManagedSerializableMemberInfo::getRangeMinimum */
+		float getRangeMinimum() const override;
+
+		/** @copydoc ManagedSerializableMemberInfo::getRangeMaximum */
+		float getRangeMaximum() const override;
+
+		/** @copydoc ManagedSerializableMemberInfo::renderAsSlider */
+		bool renderAsSlider() const override;
+
+		/** @copydoc ManagedSerializableMemberInfo::getStep */
+		float getStep() const override;
+
+		/** @copydoc ManagedSerializableMemberInfo::getValue */
+		MonoObject* getValue(MonoObject* instance) const override;
+
+		/** @copydoc ManagedSerializableMemberInfo::setValue */
+		void setValue(MonoObject* instance, void* value) const override;
+
+		MonoProperty* mMonoProperty;
+
+		/************************************************************************/
+		/* 								RTTI		                     		*/
+		/************************************************************************/
+	public:
+		friend class ManagedSerializablePropertyInfoRTTI;
+		static RTTITypeBase* getRTTIStatic();
+		RTTITypeBase* getRTTI() const override;
+	};
+
 	/** Contains data about fields of a complex object, and the object's class hierarchy if it belongs to one. */
 	class BS_SCR_BE_EXPORT ManagedSerializableObjectInfo : public IReflectable
 	{

+ 25 - 0
Source/SBansheeEngine/Include/BsManagedSerializableObjectInfoRTTI.h

@@ -202,6 +202,31 @@ namespace BansheeEngine
 		}
 	};
 
+	class BS_SCR_BE_EXPORT ManagedSerializablePropertyInfoRTTI : public RTTIType<ManagedSerializablePropertyInfo, ManagedSerializableMemberInfo, ManagedSerializablePropertyInfoRTTI>
+	{
+	private:
+
+	public:
+		ManagedSerializablePropertyInfoRTTI()
+		{ }
+
+		const String& getRTTIName() override
+		{
+			static String name = "ScriptSerializablePropertyInfo";
+			return name;
+		}
+
+		UINT32 getRTTIId() override
+		{
+			return TID_SerializablePropertyInfo;
+		}
+
+		SPtr<IReflectable> newRTTIObject() override
+		{
+			return bs_shared_ptr_new<ManagedSerializablePropertyInfo>();
+		}
+	};
+
 	class BS_SCR_BE_EXPORT ManagedSerializableTypeInfoRTTI : public RTTIType<ManagedSerializableTypeInfo, IReflectable, ManagedSerializableTypeInfoRTTI>
 	{
 	private:

+ 1 - 0
Source/SBansheeEngine/Include/BsScriptAssemblyManager.h

@@ -109,6 +109,7 @@ namespace BansheeEngine
 		MonoClass* mDontSerializeFieldAttribute;
 		MonoClass* mSerializeFieldAttribute;
 		MonoClass* mHideInInspectorAttribute;
+		MonoClass* mShowInInspectorAttribute;
 		MonoClass* mRangeAttribute;
 		MonoClass* mStepAttribute;
 	};

+ 2 - 1
Source/SBansheeEngine/Include/BsScriptEnginePrerequisites.h

@@ -163,6 +163,7 @@ namespace BansheeEngine
 		TID_ScriptModifiedDictionaryEntry = 50048,
 		TID_ScriptSerializableDictionaryKeyValue = 50049,
 		TID_SerializableTypeInfoRef = 50050,
-		TID_SerializableFieldInfo = 50051
+		TID_SerializableFieldInfo = 50051,
+		TID_SerializablePropertyInfo = 50052
 	};
 }

+ 3 - 3
Source/SBansheeEngine/Source/BsManagedSerializableDictionary.cpp

@@ -313,13 +313,13 @@ namespace BansheeEngine
 		assert(enumeratorClass != nullptr);
 
 		mEnumMoveNext = enumeratorClass->getMethod("MoveNext");
-		mEnumCurrentProp = &enumeratorClass->getProperty("Current");
+		mEnumCurrentProp = enumeratorClass->getProperty("Current");
 
 		MonoClass* keyValuePairClass = mEnumCurrentProp->getReturnType();
 		assert(keyValuePairClass != nullptr);
 
-		mKeyProp = &keyValuePairClass->getProperty("Key");
-		mValueProp = &keyValuePairClass->getProperty("Value");
+		mKeyProp = keyValuePairClass->getProperty("Key");
+		mValueProp = keyValuePairClass->getProperty("Value");
 	}
 
 	RTTITypeBase* ManagedSerializableDictionary::getRTTIStatic()

+ 2 - 2
Source/SBansheeEngine/Source/BsManagedSerializableList.cpp

@@ -194,8 +194,8 @@ namespace BansheeEngine
 
 	void ManagedSerializableList::initMonoObjects(MonoClass* listClass)
 	{
-		mItemProp = &listClass->getProperty("Item");
-		mCountProp = &listClass->getProperty("Count");
+		mItemProp = listClass->getProperty("Item");
+		mCountProp = listClass->getProperty("Count");
 		mAddMethod = listClass->getMethod("Add", 1);
 		mAddRangeMethod = listClass->getMethod("AddRange", 1);
 		mClearMethod = listClass->getMethod("Clear");

+ 91 - 0
Source/SBansheeEngine/Source/BsManagedSerializableObjectInfo.cpp

@@ -7,6 +7,7 @@
 #include "BsMonoClass.h"
 #include "BsMonoManager.h"
 #include "BsMonoField.h"
+#include "BsMonoProperty.h"
 #include "BsScriptTexture2D.h"
 #include "BsScriptSpriteTexture.h"
 #include "BsScriptAssemblyManager.h"
@@ -198,6 +199,96 @@ namespace BansheeEngine
 		return ManagedSerializableFieldInfo::getRTTIStatic();
 	}
 
+	ManagedSerializablePropertyInfo::ManagedSerializablePropertyInfo()
+		:mMonoProperty(nullptr)
+	{
+
+	}
+
+	float ManagedSerializablePropertyInfo::getRangeMinimum() const
+	{
+		if (((UINT32)mFlags & (UINT32)ScriptFieldFlags::Range) != 0)
+		{
+
+			MonoClass* range = ScriptAssemblyManager::instance().getRangeAttribute();
+			if (range != nullptr)
+			{
+				float min = 0;
+				ScriptRange::getMinRangeField()->getValue(mMonoProperty->getAttribute(range), &min);
+				return min;
+			}
+		}
+		return 0;
+	}
+
+	float ManagedSerializablePropertyInfo::getRangeMaximum() const
+	{
+		if (((UINT32)mFlags & (UINT32)ScriptFieldFlags::Range) != 0)
+		{
+
+			MonoClass* range = ScriptAssemblyManager::instance().getRangeAttribute();
+			if (range != nullptr)
+			{
+				float max = 0;
+				ScriptRange::getMaxRangeField()->getValue(mMonoProperty->getAttribute(range), &max);
+				return max;
+			}
+		}
+		return 0;
+	}
+
+	bool ManagedSerializablePropertyInfo::renderAsSlider() const
+	{
+		if (((UINT32)mFlags & (UINT32)ScriptFieldFlags::Range) != 0)
+		{
+			MonoClass* range = ScriptAssemblyManager::instance().getRangeAttribute();
+			if (range != nullptr)
+			{
+				bool slider = false;
+				ScriptRange::getSliderField()->getValue(mMonoProperty->getAttribute(range), &slider);
+				return slider;
+			}
+		}
+		return false;
+	}
+
+
+	float ManagedSerializablePropertyInfo::getStep() const
+	{
+		if (((UINT32)mFlags & (UINT32)ScriptFieldFlags::Step) != 0)
+		{
+
+			MonoClass* step = ScriptAssemblyManager::instance().getStepAttribute();
+			if (step != nullptr)
+			{
+				float value = 0;
+				ScriptStep::getStepField()->getValue(mMonoProperty->getAttribute(step), &value);
+				return value;
+			}
+		}
+		return 0;
+	}
+
+	MonoObject* ManagedSerializablePropertyInfo::getValue(MonoObject* instance) const
+	{
+		return mMonoProperty->get(instance);
+	}
+
+	void ManagedSerializablePropertyInfo::setValue(MonoObject* instance, void* value) const
+	{
+		mMonoProperty->set(instance, value);
+	}
+
+	RTTITypeBase* ManagedSerializablePropertyInfo::getRTTIStatic()
+	{
+		return ManagedSerializablePropertyInfoRTTI::instance();
+	}
+
+	RTTITypeBase* ManagedSerializablePropertyInfo::getRTTI() const
+	{
+		return ManagedSerializablePropertyInfo::getRTTIStatic();
+	}
+
 	RTTITypeBase* ManagedSerializableTypeInfo::getRTTIStatic()
 	{
 		return ManagedSerializableTypeInfoRTTI::instance();

+ 52 - 11
Source/SBansheeEngine/Source/BsScriptAssemblyManager.cpp

@@ -35,7 +35,7 @@ namespace BansheeEngine
 		, mSystemGenericDictionaryClass(nullptr), mSystemTypeClass(nullptr), mComponentClass(nullptr)
 		, mSceneObjectClass(nullptr), mMissingComponentClass(nullptr), mSerializeObjectAttribute(nullptr)
 		, mDontSerializeFieldAttribute(nullptr), mSerializeFieldAttribute(nullptr), mHideInInspectorAttribute(nullptr)
-		, mRangeAttribute(nullptr), mStepAttribute(nullptr)
+		, mShowInInspectorAttribute(nullptr), mRangeAttribute(nullptr), mStepAttribute(nullptr)
 	{
 
 	}
@@ -102,14 +102,14 @@ namespace BansheeEngine
 			}
 		}
 
-		// Populate field data
+		// Populate field & property data
 		for(auto& curClassInfo : assemblyInfo->mObjectInfos)
 		{
 			SPtr<ManagedSerializableObjectInfo> objInfo = curClassInfo.second;
 
 			UINT32 mUniqueFieldId = 1;
-			const Vector<MonoField*>& fields = objInfo->mMonoClass->getAllFields();
 
+			const Vector<MonoField*>& fields = objInfo->mMonoClass->getAllFields();
 			for(auto& field : fields)
 			{
 				if(field->isStatic())
@@ -139,6 +139,9 @@ namespace BansheeEngine
 				{
 					if (field->hasAttribute(mSerializeFieldAttribute))
 						fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Serializable);
+
+					if (field->hasAttribute(mShowInInspectorAttribute))
+						fieldInfo->mFlags = (ScriptFieldFlags)((UINT32)fieldInfo->mFlags | (UINT32)ScriptFieldFlags::Inspectable);
 				}
 
 				if (field->hasAttribute(mRangeAttribute))
@@ -150,6 +153,39 @@ namespace BansheeEngine
 				objInfo->mFieldNameToId[fieldInfo->mName] = fieldInfo->mFieldId;
 				objInfo->mFields[fieldInfo->mFieldId] = fieldInfo;
 			}
+
+			const Vector<MonoProperty*>& properties = objInfo->mMonoClass->getAllProperties();
+			for (auto& property : properties)
+			{
+				SPtr<ManagedSerializableTypeInfo> typeInfo = getTypeInfo(property->getReturnType());
+				if (typeInfo == nullptr)
+					continue;
+
+				SPtr<ManagedSerializablePropertyInfo> propertyInfo = bs_shared_ptr_new<ManagedSerializablePropertyInfo>();
+				propertyInfo->mFieldId = mUniqueFieldId++;
+				propertyInfo->mName = property->getName();
+				propertyInfo->mMonoProperty = property;
+				propertyInfo->mTypeInfo = typeInfo;
+				propertyInfo->mParentTypeId = objInfo->mTypeInfo->mTypeId;
+
+				if (!property->isIndexed())
+				{
+					if (property->hasAttribute(mSerializeFieldAttribute))
+						propertyInfo->mFlags = (ScriptFieldFlags)((UINT32)propertyInfo->mFlags | (UINT32)ScriptFieldFlags::Serializable);
+
+					if (property->hasAttribute(mShowInInspectorAttribute))
+						propertyInfo->mFlags = (ScriptFieldFlags)((UINT32)propertyInfo->mFlags | (UINT32)ScriptFieldFlags::Inspectable);
+				}
+
+				if (property->hasAttribute(mRangeAttribute))
+					propertyInfo->mFlags = (ScriptFieldFlags)((UINT32)propertyInfo->mFlags | (UINT32)ScriptFieldFlags::Range);
+
+				if (property->hasAttribute(mStepAttribute))
+					propertyInfo->mFlags = (ScriptFieldFlags)((UINT32)propertyInfo->mFlags | (UINT32)ScriptFieldFlags::Step);
+
+				objInfo->mFieldNameToId[propertyInfo->mName] = propertyInfo->mFieldId;
+				objInfo->mFields[propertyInfo->mFieldId] = propertyInfo;
+			}
 		}
 
 		// Form parent/child connections
@@ -361,8 +397,8 @@ namespace BansheeEngine
 			{
 				SPtr<ManagedSerializableTypeInfoList> typeInfo = bs_shared_ptr_new<ManagedSerializableTypeInfoList>();
 
-				MonoProperty& itemProperty = monoClass->getProperty("Item");
-				MonoClass* itemClass = itemProperty.getReturnType();
+				MonoProperty* itemProperty = monoClass->getProperty("Item");
+				MonoClass* itemClass = itemProperty->getReturnType();
 
 				if (itemClass != nullptr)
 					typeInfo->mElementType = getTypeInfo(itemClass);
@@ -379,17 +415,17 @@ namespace BansheeEngine
 				MonoMethod* getEnumerator = monoClass->getMethod("GetEnumerator");
 				MonoClass* enumClass = getEnumerator->getReturnType();
 
-				MonoProperty& currentProp = enumClass->getProperty("Current");
-				MonoClass* keyValuePair = currentProp.getReturnType();
+				MonoProperty* currentProp = enumClass->getProperty("Current");
+				MonoClass* keyValuePair = currentProp->getReturnType();
 
-				MonoProperty& keyProperty = keyValuePair->getProperty("Key");
-				MonoProperty& valueProperty = keyValuePair->getProperty("Value");
+				MonoProperty* keyProperty = keyValuePair->getProperty("Key");
+				MonoProperty* valueProperty = keyValuePair->getProperty("Value");
 
-				MonoClass* keyClass = keyProperty.getReturnType();
+				MonoClass* keyClass = keyProperty->getReturnType();
 				if(keyClass != nullptr)
 					typeInfo->mKeyType = getTypeInfo(keyClass);
 
-				MonoClass* valueClass = valueProperty.getReturnType();
+				MonoClass* valueClass = valueProperty->getReturnType();
 				if(valueClass != nullptr)
 					typeInfo->mValueType = getTypeInfo(valueClass);
 
@@ -443,6 +479,7 @@ namespace BansheeEngine
 
 		mSerializeFieldAttribute = nullptr;
 		mHideInInspectorAttribute = nullptr;
+		mShowInInspectorAttribute = nullptr;
 		mRangeAttribute = nullptr;
 		mStepAttribute = nullptr;
 	}
@@ -510,6 +547,10 @@ namespace BansheeEngine
 		if(mHideInInspectorAttribute == nullptr)
 			BS_EXCEPT(InvalidStateException, "Cannot find HideInInspector managed class.");
 
+		mShowInInspectorAttribute = bansheeEngineAssembly->getClass("BansheeEngine", "ShowInInspector");
+		if (mShowInInspectorAttribute == nullptr)
+			BS_EXCEPT(InvalidStateException, "Cannot find ShowInInspector managed class.");
+
 		mBaseTypesInitialized = true;
 	}