c_sharp_features.rst 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  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. .. code-block:: csharp
  62. public void MyCallback()
  63. {
  64. GD.Print("My callback!");
  65. }
  66. public void MyCallbackWithArguments(string foo, int bar)
  67. {
  68. GD.Print("My callback with: ", foo, " and ", bar, "!");
  69. }
  70. public void SomeFunction()
  71. {
  72. instance.Connect("MySignal", this, "MyCallback");
  73. instance.Connect(nameof(MySignalWithArguments), this, "MyCallbackWithArguments");
  74. }
  75. Emitting signals is done with the ``EmitSignal`` method.
  76. .. code-block:: csharp
  77. public void SomeFunction()
  78. {
  79. EmitSignal(nameof(MySignal));
  80. EmitSignal("MySignalWithArguments", "hello there", 28);
  81. }
  82. Notice that you can always reference a signal name with the ``nameof`` keyword (applied on the delegate itself).
  83. It is possible to bind values when establishing a connection by passing an object array.
  84. .. code-block:: csharp
  85. public int Value { get; private set; } = 0;
  86. private void ModifyValue(int modifier)
  87. {
  88. Value += modifier;
  89. }
  90. public void SomeFunction()
  91. {
  92. var plusButton = (Button)GetNode("PlusButton");
  93. var minusButton = (Button)GetNode("MinusButton");
  94. plusButton.Connect("pressed", this, "ModifyValue", new object[] { 1 });
  95. minusButton.Connect("pressed", this, "ModifyValue", new object[] { -1 });
  96. }
  97. 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>`.
  98. 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.
  99. .. code-block:: csharp
  100. public class DataObject : Godot.Object
  101. {
  102. public string Field1 { get; set; }
  103. public string Field2 { get; set; }
  104. }
  105. 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``).
  106. .. code-block:: csharp
  107. public void SomeFunction()
  108. {
  109. AddUserSignal("MyOtherSignal");
  110. EmitSignal("MyOtherSignal");
  111. }