c_sharp_features.rst 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. .. _doc_c_sharp_features:
  2. Features
  3. ========
  4. This page provides an overview of the commonly used features of both C# and Godot
  5. and how they are used together.
  6. .. _doc_c_sharp_features_type_conversion_and_casting:
  7. Type conversion and casting
  8. ---------------------------
  9. C# is a statically typed language. Therefore, you can't do the following:
  10. .. code-block:: csharp
  11. var mySprite = GetNode("MySprite");
  12. mySprite.SetFrame(0);
  13. The method ``GetNode()`` returns a ``Node`` instance.
  14. You must explicitly convert it to the desired derived type, ``Sprite`` in this case.
  15. For this, you have various options in C#.
  16. **Casting and Type Checking**
  17. Throws ``InvalidCastException`` if the returned node cannot be cast to Sprite.
  18. You would use it instead of the ``as`` operator if you are pretty sure it won't fail.
  19. .. code-block:: csharp
  20. Sprite mySprite = (Sprite)GetNode("MySprite");
  21. mySprite.SetFrame(0);
  22. **Using the AS operator**
  23. The ``as`` operator returns ``null`` if the node cannot be cast to Sprite,
  24. and for that reason, it cannot be used with value types.
  25. .. code-block:: csharp
  26. Sprite mySprite = GetNode("MySprite") as Sprite;
  27. // Only call SetFrame() if mySprite is not null
  28. mySprite?.SetFrame(0);
  29. **Using the generic methods**
  30. Generic methods are also provided to make this type conversion transparent.
  31. ``GetNode<T>()`` casts the node before returning it. It will throw an ``InvalidCastException`` if the node cannot be cast to the desired type.
  32. .. code-block:: csharp
  33. Sprite mySprite = GetNode<Sprite>("MySprite");
  34. mySprite.SetFrame(0);
  35. ``GetNodeOrNull<T>()`` uses the ``as`` operator and will return ``null`` if the node cannot be cast to the desired type.
  36. .. code-block:: csharp
  37. Sprite mySprite = GetNodeOrNull<Sprite>("MySprite");
  38. // Only call SetFrame() if mySprite is not null
  39. mySprite?.SetFrame(0);
  40. **Type checking using the IS operator**
  41. To check if the node can be cast to Sprite, you can use the ``is`` operator.
  42. The ``is`` operator returns false if the node cannot be cast to Sprite,
  43. otherwise it returns true.
  44. .. code-block:: csharp
  45. if (GetNode("MySprite") is Sprite)
  46. {
  47. // Yup, it's a sprite!
  48. }
  49. For more advanced type checking, you can look into `Pattern Matching <https://docs.microsoft.com/en-us/dotnet/csharp/pattern-matching>`_.
  50. .. _c_sharp_signals:
  51. C# signals
  52. ----------
  53. For a complete C# example, see the **Handling a signal** section in the step by step :ref:`doc_scripting` tutorial.
  54. Declaring a signal in C# is done with the ``[Signal]`` attribute on a delegate.
  55. .. code-block:: csharp
  56. [Signal]
  57. delegate void MySignal();
  58. [Signal]
  59. delegate void MySignalWithArguments(string foo, int bar);
  60. These signals can then be connected either in the editor or from code with ``Connect``.
  61. If you want to connect a signal in the editor, you need to (re)build the project assemblies to see the new signal. This build can be manually triggered by clicking the “Build” button at the top right corner of the editor window.
  62. .. code-block:: csharp
  63. public void MyCallback()
  64. {
  65. GD.Print("My callback!");
  66. }
  67. public void MyCallbackWithArguments(string foo, int bar)
  68. {
  69. GD.Print("My callback with: ", foo, " and ", bar, "!");
  70. }
  71. public void SomeFunction()
  72. {
  73. instance.Connect("MySignal", this, "MyCallback");
  74. instance.Connect(nameof(MySignalWithArguments), this, "MyCallbackWithArguments");
  75. }
  76. Emitting signals is done with the ``EmitSignal`` method.
  77. .. code-block:: csharp
  78. public void SomeFunction()
  79. {
  80. EmitSignal(nameof(MySignal));
  81. EmitSignal("MySignalWithArguments", "hello there", 28);
  82. }
  83. Notice that you can always reference a signal name with the ``nameof`` keyword (applied on the delegate itself).
  84. It is possible to bind values when establishing a connection by passing an object array.
  85. .. code-block:: csharp
  86. public int Value { get; private set; } = 0;
  87. private void ModifyValue(int modifier)
  88. {
  89. Value += modifier;
  90. }
  91. public void SomeFunction()
  92. {
  93. var plusButton = (Button)GetNode("PlusButton");
  94. var minusButton = (Button)GetNode("MinusButton");
  95. plusButton.Connect("pressed", this, "ModifyValue", new object[] { 1 });
  96. minusButton.Connect("pressed", this, "ModifyValue", new object[] { -1 });
  97. }
  98. Signals support parameters and bound values of all the `built-in types <https://docs.microsoft.com/en-us/dotnet/csharp/language-reference/keywords/built-in-types-table>`_ and Classes derived from :ref:`Godot.Object <class_Object>`.
  99. Consequently, any ``Node`` or ``Reference`` will be compatible automatically, but custom data objects will need to extend from `Godot.Object` or one of its subclasses.
  100. .. code-block:: csharp
  101. public class DataObject : Godot.Object
  102. {
  103. public string Field1 { get; set; }
  104. public string Field2 { get; set; }
  105. }
  106. Finally, signals can be created by calling ``AddUserSignal``, but be aware that it should be executed before any use of said signals (with ``Connect`` or ``EmitSignal``).
  107. .. code-block:: csharp
  108. public void SomeFunction()
  109. {
  110. AddUserSignal("MyOtherSignal");
  111. EmitSignal("MyOtherSignal");
  112. }