Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 14 Next capture
2021 2022 2023
2 captures
12 Jun 22 - 14 Jun 22
sparklines
Close Help
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
  • Asset Store
  • Get Unity

UNITY ACCOUNT

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account
  • Blog
  • Forums
  • Answers
  • Evangelists
  • User Groups
  • Beta Program
  • Advisory Panel

Navigation

  • Home
  • Products
  • Solutions
  • Made with Unity
  • Learning
  • Support & Services
  • Community
    • Blog
    • Forums
    • Answers
    • Evangelists
    • User Groups
    • Beta Program
    • Advisory Panel

Unity account

You need a Unity Account to shop in the Online and Asset Stores, participate in the Unity Community and manage your license portfolio. Login Create account

Language

  • Chinese
  • Spanish
  • Japanese
  • Korean
  • Portuguese
  • Ask a question
  • Spaces
    • Default
    • Help Room
    • META
    • Moderators
    • Topics
    • Questions
    • Users
    • Badges
  • Home /
avatar image
2
Question by dCalle · Apr 29, 2017 at 11:18 PM · editor-scriptingreflectioncustom-inspectorserializedpropertytypecasting

A smarter Way to get the Type of SerializedProperty

Hey Folks. I used the DrawDefaultInspectorWithoutScriptField by JAKJ and it gave me kind of the Idea of creating a custom Inspector, that could inject content where I want without writing everything from anew.

So now I wanted to add some functionality to the scriptableObject Fields, among them, creating a new one. Problem is, the most promising value I can get to figure out a serializedProperty's Object's type is serializedProperty.type, which is a string, giving me this: PPtr<$ScriptableObject>

So I deleted PPtr<$> and used the remaining string to use type.getType(string) with another ScriptableObject's reflected Assembly.

 string str =  serializedProperty.type.Replace("PPtr<$", "").Replace(">", "");
 string other = "Namespace." + str + ", " + typeof(OtherScriptableObject).Assembly;
 Type type = Type.GetType(other);

However, this is not safe for Scriptable Objects inside Namespaces (and I think classes), so my Question is, is there something more appropriate to get the Type of a serializedProperty's Object or Do I have to politely ask the Dev Team for a more sophisticated indicator?^^

Comment
Add comment
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users

6 Replies

· Add your reply
  • Sort: 
avatar image
8
Best Answer

Answer by Glurth · Apr 30, 2017 at 05:55 PM

NOTE: if you have a PropertyDrawr reference, or are creating a custom one- this is probably NOT the answer for you- check out CP's answer below. This answer is for those who do NOT have a PropertyDrawer to reference.


Here is how I both get the types of SerializedProperties, AND get /set the values as object's

     public static object GetValue(this SerializedProperty property)
     {
         System.Type parentType = property.serializedObject.targetObject.GetType();
         System.Reflection.FieldInfo fi = parentType.GetField(property.propertyPath);  
         return fi.GetValue(property.serializedObject.targetObject);
     }

     public static void SetValue(this SerializedProperty property,object value)
     {
         System.Type parentType = property.serializedObject.targetObject.GetType();
         System.Reflection.FieldInfo fi = parentType.GetField(property.propertyPath);//this FieldInfo contains the type.
         fi.SetValue(property.serializedObject.targetObject, value);
     }

To extract the type from the FieldInfo class use the FieldType member (https://msdn.microsoft.com/en-us/library/system.reflection.fieldinfo.fieldtype(v=vs.110).aspx)

  public static System.Type GetType(SerializedProperty property)
         {
             System.Type parentType = property.serializedObject.targetObject.GetType();
             System.Reflection.FieldInfo fi = parentType.GetField(property.propertyPath);
             return fi.FieldType;
         }

EDIT/UPDATE:

While dealing with multiple "levels" of properties, I found the above functions did NOT work as expected and returned null for any propertyPath (a member of the property parameter) that included a dot '.'.

Replace above instances of GetField with GetFieldViaPath (the function below) to account for, and handle, paths that are multiple members deep.

  public static System.Reflection.FieldInfo GetFieldViaPath(this System.Type type,string path)
     {
         System.Type parentType = type;
         System.Reflection.FieldInfo fi = type.GetField(path);
         string[] perDot = path.Split('.');
         foreach (string fieldName in perDot)
         {
             fi = parentType.GetField(fieldName);
             if (fi != null)
                 parentType = fi.FieldType;
             else
                 return null;
         }
         if (fi != null)
             return fi;
         else return null;
     }

Comment
Add comment · Show 5 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Glurth · Apr 30, 2017 at 06:02 PM 2
Share

ParentType is a poor name for that variable.. probably should have called it containingObjectType or something like that... NO inheritance stuff is actually involved here.

avatar image jdean300 · May 21, 2017 at 12:49 AM 3
Share

Your GetType function would need to be called something else - the compiler calls instance methods before it calls extension methods and so doing property.GetType() will call the GetType method from System.Object.

avatar image Glurth jdean300 · May 21, 2017 at 05:17 PM 0
Share

Removed "this" from the answer's GetType fnction params. Good Catch.

avatar image dCalle · May 21, 2017 at 08:16 AM 0
Share

Thanks mate. I've just seen the response, am looking into it now ;-)

avatar image dCalle · May 21, 2017 at 08:20 AM 0
Share

Jesus, and I just went nuts with the property.type string^^ Like looking into every namespace and nested class there is^^ Thanks mate, Will do some refactoring... sometime^^

avatar image
2

Answer by cp- · Jul 21, 2021 at 03:44 PM

As this thread is still kind of active and seems to be pretty high in google's ranking:

Just use PropertyDrawer's class member: fieldInfo.FieldType to get the type.

That's it.

Comment
Add comment · Show 4 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image Arycama · Sep 12, 2021 at 12:36 PM 0
Share

Thank you! This is all you need for custom property drawers.

avatar image Glurth · Sep 13, 2021 at 06:31 PM 1
Share

Just to clarify for others: If you ACTUALLY HAVE a reference to an instantiated PropertyDrawer that handles the type of the value stored in the SerializedProperty, or if you are DEFINING a custom property drawer to draw it, this is the correct answer. If you DON'T have one, like in the Q, you'll need to use another one of the methods presented in other answers.

avatar image cp- · Sep 14, 2021 at 05:26 PM 0
Share

@Glurth is right of course: This only works for within PropertyDrawers. I may have misread the original question, my bad!

avatar image Glurth cp- · Sep 15, 2021 at 03:16 PM 0
Share

All good- this is complex stuff! So, this answer is actually a really good one to have on the list here, particularly since people that ARE working on CustomPropertyDrawers are actually finding this page on google!

avatar image
1

Answer by bremyBBW · Dec 18, 2017 at 04:22 PM

You need to add Array

if (parentType.IsArray) { parentType = parentType.GetElementType(); }

and add private field in the function "GetField"

BindingFlags flags = BindingFlags.Instance|BindingFlags.Public | BindingFlags.NonPublic;

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image
1

Answer by Castle24 · Feb 10, 2020 at 03:45 PM

It's based on @Glurth post but also consider the multiple "level" of properties may contain array and generic list:

     public static class TypeExtension {
         public static System.Reflection.FieldInfo GetFieldViaPath (this System.Type type,
             string path) {
             var parent = type;
             var fi = parent.GetField (path);
             var paths = path.Split ('.');
 
             for (int i = 0; i < paths.Length; i++) {
                 fi = parent.GetField (paths[i]);
     
                 // there are only two container field type that can be serialized:
                 // Array and List<T>
                 if (fi.FieldType.IsArray) {
                     parent = fi.FieldType.GetElementType ();
                     i += 2;
                     continue;
                 }
 
                 if (fi.FieldType.IsGenericType) {
                     parent = fi.FieldType.GetGenericArguments () [0];
                     i += 2;
                     continue;
                 }
 
                 if (fi != null) {
                     parent = fi.FieldType;
                 } else {
                     return null;
                 }
 
             }
 
             return fi;
         }
     }

However, if the return type has derived classes. It can be more complicated.

     class A:ScritableObject { }
 
     class B : A { }
 
     class MyClass{
         A a1 = new B();
     }

If we want to retrieve the type of a1, the GetType() we talked above will return the type of A which is the base class. Because we are not using the memberinfo of instance to retrieving type. So we need the real instance to get the real type. But the instance is dynamically assigned or is depend on your object reference on the inspector. Here is my not perfect solution ( If you use the new UIElement, you should registe soem value-change event instead):

       var type = GetType (yourProperty);
       if (yourProperty.propertyType.Equals(UnityEditor.SerializedPropertyType.ObjectReference) && yourProperty.objectReferenceValue != null)
         type = dn.objectReferenceValue.GetType ();
 

It's only useful when you don't know the specific property. For example, when iterating all property. But if you already know it. You can get rid of these reflection operations and get type directly by its value.
Remember it's expensive to do reflection every time (Congratulations, if you also use IMGUI), and unity editor provides serializedobject and serializedProperty which are internal type to cach these info.


Comment
Add comment · Show 1 · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
avatar image el_trex · Mar 05, 2020 at 02:25 AM 0
Share

Thank you ! Your update fall right in time as I came Across the same issue as you just now !

avatar image
1

Answer by el_trex · Mar 07, 2020 at 11:17 PM

 public static System.Reflection.FieldInfo GetFieldViaPath(this System.Type type, string path)
     {
         var flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
         var parent = type;
         var fi = parent.GetField(path, flags);
         var paths = path.Split('.');
 
         for (int i = 0; i < paths.Length; i++)
         {
             fi = parent.GetField(paths[i], flags);
 
             // there are only two container field type that can be serialized:
             // Array and List<T>
             if (fi.FieldType.IsArray)
             {
                 parent = fi.FieldType.GetElementType();
                 i += 2;
                 continue;
             }
 
             if (fi.FieldType.IsGenericType)
             {
                 parent = fi.FieldType.GetGenericArguments()[0];
                 i += 2;
                 continue;
             }
 
             if (fi != null)
             {
                 parent = fi.FieldType;
             }
             else
             {
                 return null;
             }
 
         }
 
         return fi;
     }

First I wanna thank you all here. I Did this new mixing solution of @bremyBBW and the last iteration of @Castle24 to fix the error I got with a private, but serialized, field of mine.

Comment
Add comment · Share
10 |3000 characters needed characters left characters exceeded
▼
  • Viewable by all users
  • Viewable by moderators
  • Viewable by moderators and the original poster
  • Advanced visibility
Viewable by all users
  • 1
  • 2
  • ›

Your answer

Hint: You can notify a user about this post by typing @username

Up to 2 attachments (including images) can be used with a maximum of 524.3 kB each and 1.0 MB total.

Follow this Question

Answers Answers and Comments

81 People are following this question.

avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image avatar image

Related Questions

objectReferenceValue in SerializedProperty 5 Answers

How to have two inspectors of different types thats objects they are inspecting change 0 Answers

How do I open editor windows using internal methods? 2 Answers

Custom inspector: changing foldout's text color 3 Answers

Get Undo.DestroyObjectImmediate to work nicely when removing a List/array element that's pointing to a MonoBehaviour? 4 Answers


Enterprise
Social Q&A

Social
Subscribe on YouTube social-youtube Follow on LinkedIn social-linkedin Follow on Twitter social-twitter Follow on Facebook social-facebook Follow on Instagram social-instagram

Footer

  • Purchase
    • Products
    • Subscription
    • Asset Store
    • Unity Gear
    • Resellers
  • Education
    • Students
    • Educators
    • Certification
    • Learn
    • Center of Excellence
  • Download
    • Unity
    • Beta Program
  • Unity Labs
    • Labs
    • Publications
  • Resources
    • Learn platform
    • Community
    • Documentation
    • Unity QA
    • FAQ
    • Services Status
    • Connect
  • About Unity
    • About Us
    • Blog
    • Events
    • Careers
    • Contact
    • Press
    • Partners
    • Affiliates
    • Security
Copyright © 2020 Unity Technologies
  • Legal
  • Privacy Policy
  • Cookies
  • Do Not Sell My Personal Information
  • Cookies Settings
"Unity", Unity logos, and other Unity trademarks are trademarks or registered trademarks of Unity Technologies or its affiliates in the U.S. and elsewhere (more info here). Other names or brands are trademarks of their respective owners.
  • Anonymous
  • Sign in
  • Create
  • Ask a question
  • Spaces
  • Default
  • Help Room
  • META
  • Moderators
  • Explore
  • Topics
  • Questions
  • Users
  • Badges