Wayback Machinekoobas.hobune.stream
May JUN Jul
Previous capture 13 Next capture
2021 2022 2023
1 capture
13 Jun 22 - 13 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
7
Question by MrVerdoux · Dec 09, 2013 at 09:48 AM · readonly

readonly C# keyword not so usefull in Unity

I really love readonly keyword, but I have problems when using it in Unity.

You can only set a readonlyvariable when you declare it or in its class constructor, but you don´t normally use constructors with MonoBehaviour derived classes so its uses are much more limited. Private set is not as powerfull as true immutability, and with a good use of readonlyyou could also set it with the constructor while making it immutable!

Should Unity provide a solution for this? It would be really nice that Start or Awake allowed you to set readonly variables, but I don´t think that´s possible.

So, is there a way around for copying the behaviour that readonly provides?

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
9
Best Answer

Answer by Statement · Dec 09, 2013 at 02:59 PM

Currently, Unity does not support readonly field usage in a meaningful and straight forward ways to use. If you are working with dependency injection and want to use constructor injection then unfortunately I think you are not getting anywhere. The only way you can do this is by relying on Unity to do it for you, since Unity constructs the components and not you. Unity supports field injection, but that's the extent of serializable behaviour that Unity supports to my knowledge.

It would be nice if Unity also injected readonly fields and that there was some mechanism to define the values via editor (or inject the values during construction).

Since the readonly fields will not serialize to an asset file, it means that the only way you can populate readonly fields is through reflection at application runtime. It would be far more desirable to be able to do this in the editor, up front.

For now, try to let go over your wishes to use readonly and instead try to adapt to the constraints that Unity place on your code design. Accept that you can't for now use readonly in classes subclassing UnityEngine.Object (such as UnityEngine.MonoBehaviour or UnityEngine.ScriptableObject to name a few).

If you really want to use readonly, be prepared to set up an injection framework that deals with runtime injection of content, as well as some object that describes which instance should have what value for a given field. It gets messy quickly, since you now have to also perform runtime injection on objects you tell Unity to construct via GameObject.AddComponent, Object.Instantiate, Resources.Load etc etc.

And be aware that asking Unity to construct one thing may cause Unity to construct many things. The obvious one is prefabs. If you Instantiate a prefab, you can't tell in advance what else is going to be on there. Maybe you have multiple scripts that require injection. A less obvious one is AddComponent, where adding a component may also add a required component (via `RequireComponent`).

Comment
Add comment · Show 2 · 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 MrVerdoux · Dec 09, 2013 at 03:31 PM 1
Share

Amazing answer, it´s good that you wrote this. Everybody should ignore readonly for the moment... As you said it would be nice that Unity $$anonymous$$m included this, I don´t know how difficult it can be but it would be a great adition.

avatar image Statement · Dec 09, 2013 at 04:47 PM 2
Share

Since effectively you are configuring how to construct an object via the editor, you can think of the editor is preparing your Composition Root for you. If we can't have constructors, then at least it would indeed be nice if we could allow the editor to set up values for readonly fields and at runtime populate them from the serialized values. It would in effect behave almost the same as constructor injection, although you can't place constraints on the values injects (like, traditionally you could force throw ArgumentNullException if a required dependency was not passed to the constructor). It also gives you a way to define clearly what is intended to be set up in advance for us (via editor/designers) while clearly communicating that you shouldn't alter the structure at runtime since the readonly keyword will enforce the constraint at compile time.

avatar image
4

Answer by Bunny83 · Dec 09, 2013 at 11:15 AM

Readonly is just another compile time restriction just like private is. If someone has access to your source he can change private to public or remove the readonly. If he hasn't access to your source private and readonly gives you the same result in the sense of write-access.

If the user of your class uses reflection, everything can be altered at runtime except consts since they are compile time constants.

Comment
Add comment · Show 3 · 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 MrVerdoux · Dec 09, 2013 at 11:18 AM 2
Share

I am more worried about damage that I can cause, not so much about other people. I know that I can forget if I should set a variable or not, but the compiler won´t.

avatar image Bunny83 · Dec 09, 2013 at 01:45 PM 3
Share

Sure, but when you think about it, Unity uses the default $$anonymous$$ono C# compiler. All code could even be compiled with the .NET compiler and you just import the assemblies. Awake and Start are usual methods which can be called / recalled at any time. This would make it quite pointless to restrict the initializarion to those methods.

If you want you can still use readonly and use reflection to set the value once in Awake or Start. Unity uses a lot reflection itself when it comes to serialization.

avatar image Statement · Dec 09, 2013 at 02:03 PM 2
Share

@$$anonymous$$rVerdoux, Unity does not allow you to set readonly fields because Unity doesn't allow you to construct UnityEngine.Objects. Since readonly fields must be set in the constructor, we can see the problem and conclude that this problem is by design.

@Bunny83, that's one interesting idea you have there with reflection. It don't know how much it would be worth the hassle to go about doing it during Awake and at runtime, but if the readonly fields also serialize, then things might become a lot more attractive since you can inject the dependencies via editor scripts. I'm going to test this soon :)

Edit: I found that Unity does not serialize readonly fields into the scene, so you can't depend on Unity to serialize readonly fields to become automatically populated when Unity constructs the component, sadly.

 // Does not serialize into the .unity scene file
 public readonly int testField;

 // Does not serialize into the .unity scene file
 [SerializeField]
 public readonly int testField;

 // Does not serialize into the .unity scene file
 // Does not use $$anonymous$$yCustomPropertyDrawer for drawing either
 [SerializeField]
 [$$anonymous$$yCustomPropertyDrawer]
 public readonly int testField;

So you seem out of luck with that approach. It would however be nice if we could get this functionality by asking the editor/core $$anonymous$$m at Unity nicely via feedback that we want this.

avatar image
2

Answer by ArkaneX · Dec 09, 2013 at 10:18 AM

IMHO private/protected set is good enough to use instead. If you want to have more readonly like behavior, you could introduce additional boolean, indicating if particular variable was set. Something like this:

 private bool _isMyVariableInitialized = false;
 private string _myVariable;
 
 public string MyVariable
 {
     private set
     {
         if(_isMyVariableInitialized)
         {
             throw new Exception("Property has already been initialized.");
         }
         _myVariable = value;
         _isMyVariableInitialized = true;
     }
     get
     {
         if(!_isMyVariableInitialized)
         {
             throw new Exception("Property has not been initialized.");
         }
         return _myVariable;
     }
 }

But the question is: do you really need such complication? :)

Comment
Add comment · Show 3 · 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 MrVerdoux · Dec 09, 2013 at 10:25 AM 1
Share

I agree with you that I don´t need such complication. If I had to do this I´d prefer private set, but if I could choose, private immutability is still my favourite. I find it very expresive, it´s a constant that you can set at construction time, I really like it.

edit: and even with this method, you can still acess _myVariable privately without using the property, and the boolean as well, so I don´t think it mimics very well readonly.

avatar image ArkaneX · Dec 09, 2013 at 11:49 AM 1
Share

I agree that this is not a bulletproof solution, but I'm afraid you won't find any that really is...

avatar image MrVerdoux · Dec 09, 2013 at 12:19 PM 0
Share

I´m afraid I agree with you. I´ll just use private set, which is pretty solid anyway, this question was just to find out people´s solutions, in case anybody had already thought of using readonly in with monobehaviour.

avatar image
1

Answer by Jamora · Dec 09, 2013 at 10:55 AM

You will need to separate your data (more accurately: state) from your MonoBehaviours. Use a normal (non-MB) class to store your readonly data, and have a reference to that object in your MB.

 [RequireComponent(typeof(Rigidbody))]
 public class NewBehaviourScript : MonoBehaviour {
  
     public MyData data;
     
     void Awake(){
         data = new MyData(rigidbody);
     }
 
     public Rigidbody GetReadOnlyRigidbody(){ return data.rigidbody; }
     
     void Reset(){
         data = new MyData(rigidbody);
     }
 
 
 private class MyData {
 
     public readonly Rigidbody rigidbody;
     
     private MyData(){}
     
     public MyData(Rigidbody rb){
         rigidbody = rb;
     } 
 }
 }

The only way for damage to change during runtime, when doing it like this, is if another developer extends MyData, changes the constructor and substitutes the original fioeld with the extended instance. As a precaution, you could seal MyData (using the sealed keyword), but that's only effective if the source code can't be accessed by other developers (they could just remove the sealed keyword).

Edit:

This solution does prevent the developer from messing up the value. Altough, I concede that it's not 100% idiot proof. If I ever needed a readonly field, this is most likely how I'd go about it.

The readonly values are set in the constructor, so even if you instantiate a new MyData, the value still can not be null. Because the field is readonly inside MyData, it cannot be changed during runtime from elsewhere in the code (well,reflection). The only way for a developer to mess this up, is to give the object a wrong RigidBody, say from another gameObject, but that sort of an error would probably have happened even if Unity allowed constructors in MBs.

I do agree with Bunny in that having only a getter is enough, more often than not.

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 MrVerdoux · Dec 09, 2013 at 12:21 PM 0
Share

That´s smart. It does´t seem ideal, but at least this gets readonly to work properly. Thank you!

avatar image Bunny83 · Dec 09, 2013 at 02:02 PM 1
Share

Yes, this is a solution of some sort, but it's a bad example ;) readonly for primitive types works well even in Unity. The problem are reference types to components and other Unity classes which can't be created / accessed in the constructor of the $$anonymous$$onoBehaviour and have to be initialized later.

As the example is at the moment it doesn't secure the value at all ;)

From outside Enemy you can't mess up the value, but from inside you can still replace "data" at any time or even set it null.

Sure, why would you do this... but the point was to restrict yourself from not be able to mess up the value. This is more an attempt in the category "security through obscurity". It adds complexity but no security.

In the end it always depends on your discipline. A "readonly" property (only a getter) and a private variable are in most cases more than enough. If the class is designed right you should use the interface it provides.

avatar image Statement · Dec 09, 2013 at 02:14 PM 0
Share

This very simple example could have been solved with const.

avatar image MrVerdoux · Dec 09, 2013 at 02:23 PM 0
Share

@Bunny83 You are completly right, I didn´t fully understood the code! This causes the same problem as before... I´m sorry Jamora but I will take the correct answer from you. As Bunny83 is saying things can still be set privately, and not only that, now the setters are in other class so it´s a lot more obscure.

avatar image
-1

Answer by yoyo · May 22, 2014 at 04:21 PM

As others have mentioned, you can use the readonly keyword just fine in classes that do not subclass from MonoBehaviour or ScriptableObject, as this is just regular C# usage.

For types derived from MonoBehaviour and ScriptableObject, the lack of a constructor means readonly is effectively similar to const -- you can provide an initializer, but you don't get a chance to modify the value in a constructor.

However, since readonly fields are accessible through reflection, it is possible to modify their values after construction (though this violates the intent of the keyword). Here is an extension class that lets you modify any field of any object, including readonly and private fields:

 public static class FieldSetterExtension
 {
     public static void SetField<T>(this object obj, string field, T val)
     {
         obj
             .GetType()
             .GetField(field, System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.Instance)
             .SetValue(obj, val);
     }
 }

With this compiled into your project you can now do things like:

 public class ReadOnlyComponent : MonoBehaviour
 {
     public readonly int CantTouchThis = 1;
 
     void Awake()
     {
         this.SetField<int>("CantTouchThis", 42);
     }
 }

Yes, this is a dangerous extension method that will let you do all sorts of nefarious things, use at your own risk!

The problem remains that Unity does not serialize read-only fields, so the CantTouchThis field above cannot be edited in the Inspector. Since readonly fields are accessible via reflection, there's no particular reason Unity shouldn't be able to serialize them, and it seems to me like a good feature to request.

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

22 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

Related Questions

A node in a childnode? 1 Answer

Build DLL using Visual Scripting Tool? 0 Answers

How to write a shortcut for MonoDevelop editor? 1 Answer

How do make a Instantiated prefab a child of exsisting Object? 3 Answers

projection/trajectory motion Unity3d 1 Answer


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