Thread Status:
Not open for further replies.
  1. Wulf

    Wulf Community Admin Community Admin Oxide Developer

    Our community code wizards are at it again! With the help of @bawNg and @Nogrod, we've managed to do away with the .NET dependency and upgrade the supported C# version in the process! The latest Oxide build bundles the latest Mono compiler and required necessities to compile CSharp plugins, without having to install Microsoft .NET Framework x64 anymore. While this does mean the Oxide builds will be slightly larger (~4mb more), we feel like it is worth it for an easier setup, Linux compiler support, and the support for the newer C# syntax.

    Want to give it a try? Download the latest snapshot from GitHub and code away! Keep in mind that Visual Studio 2015 is required for developing your plugins in order to avoid the syntax being thought invalid.

    Now a little about the new syntax features supported... C# 4 introduced optional and named method arguments. C# 6 introduced many new syntax shortcuts which reduce boilerplate code and allow you to write cleaner plugins using less lines of code. Below is an overview of some of the more useful features which plugin developers are now be able to use.

    Optional method arguments:
    It's now possible to define and use methods which have optional arguments.
    Code (C#):
    // Definition Before:
    void SendHelpToPlayer(BasePlayer player, bool already_welcomed)
    {
        if (already_welcomed)
            SendHelpToPlayer(player);
        else
            player.ChatMessage("Welcome!");
    }
    void SendHelpToPlayer(BasePlayer player)
    {
        player.ChatMessage("Help goes here");
    }

    // Definition After:
    void SendHelpToPlayer(BasePlayer player, bool already_welcomed = false)
    {
        player.ChatMessage(already_welcomed ? "Help goes here" : "Welcome!");
    }
    Code (C#):
    // Usage Before:
    player.Kill(BaseNetworkable.DestroyMode.None);

    // Usage After:
    player.Kill();
    Named method arguments:
    You can skip optional arguments when calling methods by using named arguments.
    Code (C#):
    // Before:
    ItemManager.CreateByItemID(item_id, 1, true);

    // After:
    ItemManager.CreateByItemID(item_id, isBlueprint: true);
    String interpolation:
    Code (C#):
    // You previously had to use string.Format:
    var message = string.Format("{0}: {1}", key, value);

    // The new string interpolation syntax calls string.Format for you:
    var message = $"{key}: {value}";
    Null-conditional operator:
    Code (C#):
    // Before:
    private string GetPlayerAddress(BasePlayer player)
    {
        var address = "unknown";
        if (player != null && player.net != null && player.net.connection != null)
            address = player.net.connection.ipaddress;
            return address;
    }

    // After:
    private string GetPlayerAddress(BasePlayer player)
    {
        return player?.net?.connection?.ipaddress ?? "unknown";
    }
    You can also use this operator when referencing an index:
    Code (C#):
    dict?["foo"]?["bar"]
    This is especially useful when invoking a thread-safe callback:
    Code (C#):
    // Before:
    Action<bool> complete_callback = OnComplete;
    if (complete_callback != null) complete_callback(succeeded);

    // After:
    OnComplete?(succeeded)
    New index initializer syntax:
    You can now initialize indexes using a more natural syntax.
    Code (C#):
    var weaponAliases = new Dictionary<string, string>
    {
        ["ak47"] = "rifle_ak",
        ["bolt"] = "rifle_bolt"
    };
    Expression bodied methods and properties:
    You can now implement simple methods and properties using an expression instead of a statement body.
    Code (C#):
    // Method example:
    public override string ToString() => $"Plugin: {Name}";
    // Property example:
    public float Now => Time.realtimeSinceStartup;
    Exception filters:
    Code (C#):
    try
    {
        ...
    }
    catch (Exception ex) if (ex.Message == "Specific message")
    {
        ...
    }
    Static using statements:
    You can now import static classes and all static members will be imported directly into the scope.
    Code (C#):
    using System.Math;
    class EasyMath : RustPlugin
    {
        void Loaded()
        {
            Puts(Sqrt(3*3 + 4*4));
        }
    }
    Auto-Property Initializers:
    Code (C#):
    public string Config { get; } = string.IsNullOrWhiteSpace(string connectionString = (string)Settings.Context?["connectionString"]) ? connectionString : "<none>";
    Nameof Expressions:
    Never forget to update the name of a variable or class used in a string again.
    Code (C#):
    void ThrowArgumentNullException(string parameter_name)
    {
        throw new ArgumentNullException(nameof(parameter_name));
    }
    Some other changes:
    • Overload resolution has been improved in a few ways, which will likely result in more things just working the way you'd expect them to. The improvements all relate to "betterness" - the way the compiler decides which of two overloads is better for a given argument.
    • It is now possible to have parameter-less constructors in structs.
     
Thread Status:
Not open for further replies.