Freigeben über


Codierungsrichtlinien

In diesem Dokument werden die empfohlenen Codierungsrichtlinien für World Locking Tools for Unity beschrieben. Die meisten dieser Vorschläge folgen den empfohlenen Standards von MSDN.


Skriptheader mit Lizenzinformationen

Alle in World Locking Tools for Unity geposteten Skripts müssen den standardmäßigen Lizenzheader enthalten, genau wie unten gezeigt:

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License. See LICENSE in the project root for license information.

Alle ohne Lizenzheader übermittelten Skriptdateien werden abgelehnt.

Header mit Funktions-/Methodenzusammenfassung

Alle bereitgestellten öffentlichen Klassen, Strukturen, Enumerationen, Funktionen, Eigenschaften und Felder müssen hinsichtlich ihres Zwecks und ihrer Verwendung beschrieben werden, genau wie unten dargestellt:

    /// <summary>
    /// The Controller definition defines the Controller as defined by the SDK / Unity.
    /// </summary>
    public struct Controller
    {
        /// <summary>
        /// The ID assigned to the Controller
        /// </summary>
        public string ID;
    }

Diese Regel stellt sicher, dass die Dokumentation für alle Klassen, Methoden und Eigenschaften ordnungsgemäß erstellt und verteilt wird.

Alle ohne ordnungsgemäße Zusammenfassung übermittelten Skriptdateien werden abgelehnt.

Namespaceregeln

Alle Klassen und Erweiterungen müssen auf einen Namespace festgelegt werden, wobei eine geeignete Auswahl aus den folgenden Namespaces zu treffen ist.

Microsoft.MixedReality.WorldLocking.Core: Basiscode, der den grundlegenden Dienst der World Locking Tools erfüllt.

Microsoft.MixedReality.WorldLocking.Tools: Optionale Funktionen zur Ergänzung der Entwicklung auf der Basis von World Locking Tools. Beispiele sind Diagnosevisualisierungen und Basisimplementierungen von Anwendungsereignishandlern.

Microsoft.MixedReality.WorldLocking.Examples: Spezifische Implementierungen, die die Verwendung der World Locking Tools-Features veranschaulichen und die damit erzielten Vorteile aufzeigen.

Zugehörige Features innerhalb von einem der oben genannten Namespaces können durch Erweiterung in einem neuen untergeordneten Namespace gruppiert werden.

Empfohlen

namespace Microsoft.MixedReality.WorldLocking.Examples.Placement
{
    // Interface, class or data type definition.
}

Wenn Sie den Namespace für eine Schnittstelle, eine Klasse oder einen Datentyp weglassen, wird Ihre Änderung blockiert.

Leerzeichen anstelle von Tabstopps

Achten Sie bei Ihren Beiträgen zu diesem Projekt darauf, dass Sie vier Leerzeichen anstelle von Tabstopps verwenden.

Stellen Sie außerdem sicher, dass Leerzeichen für bedingte Funktionen/Schleifenfunktionen wie „if“, „while“, „for“ hinzugefügt werden.

Sie sollten auf keinen Fall

private Foo () // < - space between Foo and ()
{
    if(Bar==null) // <- no space between if and ()
    {
        DoThing();
    }
    
    while(true) // <- no space between while and ()
    {
        Do();
    }
}

Sie sollten

private Foo()
{
   if (Bar==null)
   {
       DoThing();
   }
   
   while (true)
   {
       Do();
   }
}

Abstand

Fügen Sie keine zusätzlichen Leerzeichen zwischen eckigen Klammern und runden Klammern hinzu:

Sie sollten auf keinen Fall

private Foo()
{
    int[ ] var = new int [ 9 ];
    Vector2 vector = new Vector2 ( 0f, 10f );
}

Sie sollten

private Foo()
{
    int[] var = new int[9];
    Vector2 vector = new Vector2(0f, 10f);
}

Namenskonventionen

Verwenden Sie immer PascalCase für öffentliche/geschützte/virtuelle Eigenschaften und camelCase für private Eigenschaften und Felder.

Die einzige Ausnahme sind Datenstrukturen, bei denen die Felder über JsonUtility serialisiert werden müssen.

Sie sollten auf keinen Fall

public string myProperty; // <- Starts with a lower case letter
private string MyProperty; // <- Starts with an uppercase case letter

Sie sollten

public string MyProperty;
protected string MyProperty;
private string myProperty;

Zugriffsmodifizierer

Deklarieren Sie immer einen Zugriffsmodifizierer für alle Felder, Eigenschaften und Methoden.

Alle Unity-API-Methoden sollten standardmäßig als private definiert sein – es sei denn, Sie müssen sie in einer abgeleiteten Klasse überschreiben. In diesem Fall muss protected verwendet werden.

Felder sollten immer private privat sein, mit Eigenschaftenzugriff public oder protected.

Sie sollten auf keinen Fall

// protected field should be private
protected int myVariable = 0;

// property should have protected setter
public int MyVariable { get { return myVariable; } }

// No public / private access modifiers
void Foo() { }
void Bar() { }

Sie sollten

public int MyVariable { get; protected set; } = 0;

private void Foo() { }
public void Bar() { }
protected virtual void FooBar() { }

Verwenden von geschweiften Klammern

Verwenden Sie nach jedem Anweisungsblock stets geschweifte Klammern, und platzieren Sie sie in der nächsten Zeile.

Tue nicht

private Foo()
{
    if (Bar==null) // <- missing braces surrounding if action
        DoThing();
    else
        DoTheOtherThing();
}

Sie sollten auf keinen Fall

private Foo() { // <- Open bracket on same line
    if (Bar==null) DoThing(); <- if action on same line with no surrounding brackets 
    else DoTheOtherThing();
}

Sie sollten

private Foo()
{
    if (Bar==true)
    {
        DoThing();
    }
    else
    {
        DoTheOtherThing();
    }
}

Öffentliche Klassen, Strukturen und Enumerationen müssen in eigenen Dateien gespeichert werden.

Wenn die Klasse, die Struktur oder die Enumeration als privat gekennzeichnet werden kann, dann kann sie in dieselbe Datei aufgenommen werden. Durch diese Einbindung werden Kompilierungsprobleme mit Unity vermieden und eine ordnungsgemäße Codeabstraktion gewährleistet. Außerdem werden Konflikte und Breaking Changes reduziert, wenn Code geändert werden muss.

Sie sollten auf keinen Fall

public class MyClass
{
    public struct MyStruct() { }
    public enum MyEnumType() { }
    public class MyNestedClass() { }
}

Sie sollten

// Private references for use inside the class only
public class MyClass
{
   private struct MyStruct() { }
   private enum MyEnumType() { }
   private class MyNestedClass() { }
}

Empfohlen

MyStruct.cs

// Public Struct / Enum definitions for use in your class.  Try to make them generic for reuse.
public struct MyStruct
{
   public string Var1;
   public string Var2;
}

MyEnumType.cs

public enum MuEnumType
{
    Value1,
    Value2 // <- note, no "," on last value to denote end of list.
}

MyClass.cs

public class MyClass
{
    private MyStruct myStructreference;
    private MyEnumType myEnumReference;
}

Anordnen von Enumerationen für eine ordnungsgemäße Erweiterung

Wenn eine Enumeration in Zukunft erweitert werden soll, ist es wichtig, die Standardwerte am Anfang der Enumeration anzuordnen. So wird sichergestellt, dass Enumerationsindizes nicht durch neue Hinzufügungen beeinträchtigt werden.

Sie sollten auf keinen Fall

public enum SDKType
{
    WindowsMR,
    OpenVR,
    OpenXR,
    None, <- default value not at start
    Other <- anonymous value left to end of enum
}

Sie sollten

   /// <summary>
   /// The SDKType lists the VR SDK's that are supported by the MRTK
   /// Initially, this lists proposed SDK's, not all may be implemented at this time (please see ReleaseNotes for more details)
   /// </summary>
   public enum SDKType
   {
       /// <summary>
       /// No specified type or Standalone / non-VR type
       /// </summary>
       None = 0,
       /// <summary>
       /// Undefined SDK.
       /// </summary>
       Other,
       /// <summary>
       /// The Windows 10 Mixed reality SDK provided by the Universal Windows Platform (UWP), for Immersive MR headsets and HoloLens. 
       /// </summary>
       WindowsMR,
       /// <summary>
       /// The OpenVR platform provided by Unity (does not support the downloadable SteamVR SDK).
       /// </summary>
       OpenVR,
       /// <summary>
       /// The OpenXR platform. SDK to be determined once released.
       /// </summary>
       OpenXR
   }

Enumerationsnamen müssen auf „Type“ enden

Die Art einer Enumeration sollte durch die Verwendung des Suffixes „Type“ im Namen eindeutig angegeben werden.

Sie sollten auf keinen Fall

public enum Ordering
{
    First,
    Second,
    Third
}
public enum OrderingEnum
{
    First,
    Second,
    Third
}

Sie sollten

public enum OrderingType
{
    First = 0,
    Second,
    Third
}

Überprüfen der Verwendung von Bitfeldern für eine Enumeration

Wenn eine Enumeration mehrere Zustände als Wert erfordert, z. B. "Händigkeit = Links & Rechts". dann muss die Enumeration mit BitFlags versehen werden, damit sie ordnungsgemäß verwendet werden kann.

Die Datei „Handedness.cs“ enthält eine konkrete Implementierung für diesen Fall.

Sie sollten auf keinen Fall

public enum Handedness
{
    None,
    Left,
    Right
}

Sie sollten

[flags]
public enum HandednessType
{
   None = 0 << 0,
   Left = 1 << 0,
   Right = 1 << 1,
   Both = Left | Right
}

Best Practices, einschließlich Unity-Empfehlungen

Einige der Zielplattformen dieses Projekts erfordern eine Berücksichtigung der Leistung. Vor diesem Hintergrund sollten Sie bei der Speicherzuweisung in häufig aufgerufenem Code mit kurzen Aktualisierungsschleifen oder Algorithmen vorsichtig sein.

Kapselung

Verwenden Sie immer private Felder und öffentliche Eigenschaften, wenn von außerhalb der Klasse oder Struktur auf das Feld zugegriffen werden muss. Achten Sie darauf, dass das private Feld und die öffentliche Eigenschaft nebeneinander platziert werden. So ist auf einen Blick zu erkennen, worauf die Eigenschaft beruht und dass das Feld per Skript geändert werden kann.

Wenn Sie das Feld im Inspector bearbeiten möchten, empfiehlt es sich, die Regeln für die Kapselung zu befolgen und das zugrunde liegende Feld zu serialisieren.

Die einzige Ausnahme sind Datenstrukturen, bei denen die Felder über JsonUtility serialisiert werden müssen, wobei eine Datenklasse zwingend alle öffentlichen Felder enthalten muss, damit die Serialisierung funktioniert.

Sie sollten auf keinen Fall

public float MyValue;

Sie sollten

// private field, only accessible within script (field is not serialized in Unity)
private float myValue;

Tun

// Enable private field to be configurable only in editor (field is correctly serialized in Unity)
[SerializeField] 
private float myValue;

Tue nicht

private float myValue1;
private float myValue2;

public float MyValue1
{
    get{ return myValue1; }
    set{ myValue1 = value }
}

public float MyValue2
{
    get{ return myValue2; }
    set{ myValue2 = value }
}

Sie sollten

// Enable field to be configurable in the editor and available externally to other scripts (field is correctly serialized in Unity)
[SerializeField]
[ToolTip("If using a tooltip, the text should match the public property's summary documentation, if appropriate.")]
private float myValue; // <- Notice we co-located the backing field above our corresponding property.

/// <summary>
/// If using a tooltip, the text should match the public property's summary documentation, if appropriate.
/// </summary>
public float MyValue
{
    get{ return myValue; }
    set{ myValue = value }
}

Verwenden von for anstelle von foreach (falls möglich)

In einigen Fällen ist „foreach“ erforderlich, z. B. beim Durchlaufen von „IEnumerable“. Aus Leistungsgründen sollten Sie „foreach“ jedoch nach Möglichkeit vermeiden.

Sie sollten auf keinen Fall

foreach(var item in items)

Sie sollten

int length = items.length; // cache reference to list/array length
for(int i=0; i < length; i++)

Zwischenspeichern von Werten und Serialisierung in Szene/Prefab (falls möglich)

Mit Blick auf das HoloLens-System empfiehlt es sich, die Leistung zu optimieren und Verweise in der Szene oder dem Prefab zwischenzuspeichern, um die Speicherbelegung während der Laufzeit zu begrenzen.

Sie sollten auf keinen Fall

void Update()
{
    gameObject.GetComponent<Renderer>().Foo(Bar);
}

Sie sollten

[SerializeField] // To enable setting the reference in the inspector.
private Renderer myRenderer;

private void Awake()
{
   // If you didn't set it in the inspector, then we cache it on awake.
   if (myRenderer == null)
   {
       myRenderer = gameObject.GetComponent<Renderer>();
   }
}

private void Update()
{
   myRenderer.Foo(Bar);
}

Zwischenspeichern von Materialverweisen anstelle eines immer neuen Aufrufs von „.material“

Unity erstellt bei jeder Verwendung von „.material“ neues Material, was zu einem Speicherleck führt, wenn keine ordnungsgemäße Bereinigung erfolgt.

Sie sollten auf keinen Fall

public class MyClass
{
    void Update() 
    {
        Material myMaterial = GetComponent<Renderer>().material;
        myMaterial.SetColor("_Color", Color.White);
    }
}

Sie sollten

// Private references for use inside the class only
public class MyClass
{
   private Material cachedMaterial;

   private void Awake()
   {
       cachedMaterial = GetComponent<Renderer>().material;
   }

   void Update() 
   {
       cachedMaterial.SetColor("_Color", Color.White);
   }
   
   private void OnDestroy()
   {
       Destroy(cachedMaterial);
   }
}

Verwenden Sie alternativ die Unity-Eigenschaft „SharedMaterial“, die nicht bei jedem Verweis neues Material erstellt.

Verwenden einer plattformabhängigen Kompilierung, um Buildfehler durch das Toolkit auf anderen Plattformen zu vermeiden

  • Verwenden Sie WINDOWS_UWP, um UWP-spezifische, Nicht-Unity-APIs zu nutzen. Diese Definition verhindert die Ausführung im Editor oder auf nicht unterstützten Plattformen. Diese Definition ist äquivalent zu UNITY_WSA && !UNITY_EDITOR und sollte bevorzugt verwendet werden.
  • Verwenden Sie UNITY_WSA, um UWP-spezifische Unity-APIs zu nutzen, z. B. den UnityEngine.XR.WSA-Namespace. Die Ausführung erfolgt im Editor, wenn die Plattform auf UWP festgelegt ist, und in erstellten UWP-Apps.

Anhand der folgenden Tabelle können Sie entscheiden, welche #if-Option Sie in Abhängigkeit von Ihren Anwendungsfällen und den erwarteten Buildeinstellungen verwenden sollten.

Definieren UWP IL2CPP UWP .NET Editor
UNITY_EDITOR False Falsch Wahr
UNITY_WSA True True True
WINDOWS_UWP True True Falsch
UNITY_WSA && !UNITY_EDITOR Wahr True Falsch
ENABLE_WINMD_SUPPORT Wahr True False
NETFX_CORE Falsch True Falsch

„DateTime.UtcNow“ gegenüber „DateTime.Now“ bevorzugen

„DateTime.UtcNow“ ist schneller als „DateTime.Now“. In vergangenen Leistungsuntersuchungen haben wir festgestellt, dass die Verwendung von „DateTime.Now“ erheblichen Zusatzaufwand verursacht, insbesondere bei Verwendung in der Update()-Schleife. Dieses Problem ist auch bei anderen Benutzern aufgetreten.

Verwenden Sie lieber „DateTime.UtcNow“ – es sei denn, Sie benötigen tatsächlich die lokalisierten Zeiten (ein legitimer Grund könnte sein, dass Sie die aktuelle Zeit in der Zeitzone des Benutzers anzeigen möchten). Bei relativen Zeitangaben (d. h. dem Delta zwischen der letzten Aktualisierung und dem aktuellen Zeitpunkt) sollte am besten, „DateTime.UtcNow“ verwendet werden, um den Zusatzaufwand für die Zeitzonenumrechnung zu vermeiden.