如何使用我们的翻译器提高 Java 代码质量

我们将讨论 C# 中的方法和语言结构:哪些使用好,哪些不好。 当然,在好或不好下,我们考虑以下因素:从 C# 翻译后生成的 Java 代码的可读性和可维护性如何。

C# 中的紧凑型 – Java 中的大型型

C# 有一些紧凑的语言结构,可以完成很多隐藏的工作。当您将这些结构翻译成另一种语言时,您必须隐式地再现这些隐藏的部分,而在大多数情况下,代码确实会失去其原始设计,并且差异很大。

自动属性

自动属性在 C# 程序员中的应用非常广泛。使用这种属性,程序员可以通过 get 和/或 set 方法与隐藏字段交互。C# 确实可以让我们从自动属性的实际实现中分心,并使用非常紧凑的结构来声明它们。但是,在 Java 中,我们没有这样的语言结构,因此有必要明确地声明属性:作为字段和访问控制方法:

public int Value { get; set; }

在Java中变成:

private int auto_Value;
public int get_Value()
{
    return auto_Value;
}
public void set_Value(int value)
{
    auto_Value = value;
}

现在它不是一个完整的成员,而是三个独立的成员。想象一下,对于每个自动属性,相同的代码都会重复,它会是什么样子?让我们避免这样的不愉快的经历。但是怎么做呢?
尝试用一个对象来替换自动属性,该对象可以提供对私有字段的访问控制。可能有一个这样对象的哈希表。如果我们遵循对某些数据进行有限访问的设计,这将是一个好方法。自动属性可能看起来不错,但我们没有必要在没有必要的情况下使用它们。

值类型

在 C# 中,我们为结构体(值类型)设计了专门的内存逻辑。结构体的生命周期受限于栈帧或包含对象的生命周期,而且结构体被频繁复制–当我们将结构体作为函数参数传递、从函数返回或赋值给某个字段时,我们使用的是值而不是引用。改变复制时,我们不会改变原始值。将值类型转换为 Java 时,我们必须重新创建相同的逻辑,尽管 Java 类始终是引用类型。频繁复制现在成了一个问题–为了存储每个副本,我们都要从堆中分配内存,这就给垃圾回收器带来了过多负担。如果我们将性能作为我们的兴趣之一,我们就必须将 C# 代码从内存管理细节中抽象出来。但如何抽象呢?
最简单的方法就是让值类型不可变。如果没有可变状态,就没有必要复制该状态,以防止出现不确定的行为。

纯语法结构体

现在是时候讨论仅改变代码的视觉属性而不改变行为的语言构造了。 例如:

public class Item
{
    string name;
    string price;
    
    public Item(string name, int price) => (this.name, this.price) = (name, price);
    
    public string ToString() => $"Name = {name}, Price = {price}";
    public string Name => name;
    public int Price => price;
}

我们在这里看到了元组解构(表达式实际上并不创建元组)、插值字符串字面、表达式方法和属性。

委托

由于委托是方法声明的一种简短形式,因此非常适合使用。让我们看看下面的示例:

using System;
using System.Linq;

class Program
{
    delegate int ChangeNumber(int arg);
	static void Main()
	{
	    Console.WriteLine("Input some numbers");
        int[] numbers = Console.ReadLine().Split(" ").Select(int.Parse).ToArray();
        Console.WriteLine("Input addition");
        int addition = int.Parse(Console.ReadLine());
        ChangeNumbers(n => n + addition, numbers);
        Console.WriteLine("Result :");
        Console.WriteLine(string.Join(" ", numbers.Select(n => n.ToString())));
	}
	static void ChangeNumbers(ChangeNumber change, int[] numbers)
	{
	    for(int i = 0; i < numbers.Length; i++)
	    {
	        numbers[i] = change(numbers[i]);
	    }
	}
}

对于那个 n => n + addition 表达式,我们可以生成 Java 匿名类表达式:

// translated to Java code
interface ChangeNumber
{
    int invoke(int arg);
}
// ...static void main(String[] args)...

// anonymous class expression for Java 7 or older version
changeNumbers(new ChangeNumber()
{
    public int invoke(int n)
    {
        return n + addition;
    }
}, numbers);

// or lambda expression for higher Java 8 or newer version
changeNumbers(n -> n + addition, numbers);

得出结论

C# 有许多语言结构,它们可能会让我们的代码看起来很简单,在语法糖的背后隐藏着庞大的实现。其中一些构造值得商榷,几乎不值得支持,而另一些则非常灵活,易于重现。在设计时,最好通过对象而不是 C# 专用语言结构来组成抽象。 当谈到性能时,我们应该保持对内存管理的抽象,从而避免通过双重成本来模拟内存管理。

相关文章