C # Array.IndexOf - статическая функция, почему? [Дубликат]

Я понимаю, что это очень старое сообщение и уже есть отличные ответы, но я хотел сделать простой MCVE, чтобы продемонстрировать один такой подход и позволить новым кодеру быстро увидеть концепцию в действии.

В этом примере мы будем использовать 5 файлов:

  1. Main.java - просто используется для запуска приложения и вызова первого контроллера.
  2. Controller1.java - контроллер для первого макета FXML.
  3. Controller2.java - контроллер для второго макета FXML.
  4. Layout1.fxml - макет FXML для первого сцена.
  5. Layout2.fxml - макет FXML для второй сцены.

Все файлы перечислены полностью в нижней части этого сообщения.

Цель: показать переданные значения от Controller1 до Controller2 и наоборот.

Поток программы:

  • Первая сцена содержит TextField, Button и Label. Когда щелчок Button щелкнут, второе окно загружается и отображается, включая текст, введенный в TextField.
  • Внутри второй сцены есть также TextField, Button , и Label. Label отобразит текст, введенный в TextField на первой сцене.
  • При вводе текста во второй сцене TextField и нажатии Button первая сцена Label сцены обновлен, чтобы показать введенный текст.

Это очень простая демонстрация и, безусловно, может означать некоторое улучшение, но должна сделать концепцию очень понятной.

Сам код также комментируется некоторые детали того, что происходит и как.

КОД

Main.java:

import javafx.application.Application;
import javafx.stage.Stage;

public class Main extends Application {

    public static void main(String[] args) {
        launch(args);
    }

    @Override
    public void start(Stage primaryStage) {

        // Create the first controller, which loads Layout1.fxml within its own constructor
        Controller1 controller1 = new Controller1();

        // Show the new stage
        controller1.showStage();

    }
}

Controller1.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller1 {

    // Holds this controller's Stage
    private final Stage thisStage;

    // Define the nodes from the Layout1.fxml file. This allows them to be referenced within the controller
    @FXML
    private TextField txtToSecondController;
    @FXML
    private Button btnOpenLayout2;
    @FXML
    private Label lblFromController2;

    public Controller1() {

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout1.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout1");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    /**
     * The initialize() method allows you set setup your scene, adding actions, configuring nodes, etc.
     */
    @FXML
    private void initialize() {

        // Add an action for the "Open Layout2" button
        btnOpenLayout2.setOnAction(event -> openLayout2());
    }

    /**
     * Performs the action of loading and showing Layout2
     */
    private void openLayout2() {

        // Create the second controller, which loads its own FXML file. We pass a reference to this controller
        // using the keyword [this]; that allows the second controller to access the methods contained in here.
        Controller2 controller2 = new Controller2(this);

        // Show the new stage/window
        controller2.showStage();

    }

    /**
     * Returns the text entered into txtToSecondController. This allows other controllers/classes to view that data.
     */
    public String getEnteredText() {
        return txtToSecondController.getText();
    }

    /**
     * Allows other controllers to set the text of this layout's Label
     */
    public void setTextFromController2(String text) {
        lblFromController2.setText(text);
    }
}

Controller2.java:

import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.scene.Scene;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.TextField;
import javafx.stage.Stage;

import java.io.IOException;

public class Controller2 {

    // Holds this controller's Stage
    private Stage thisStage;

    // Will hold a reference to the first controller, allowing us to access the methods found there.
    private final Controller1 controller1;

    // Add references to the controls in Layout2.fxml
    @FXML
    private Label lblFromController1;
    @FXML
    private TextField txtToFirstController;
    @FXML
    private Button btnSetLayout1Text;

    public Controller2(Controller1 controller1) {
        // We received the first controller, now let's make it usable throughout this controller.
        this.controller1 = controller1;

        // Create the new stage
        thisStage = new Stage();

        // Load the FXML file
        try {
            FXMLLoader loader = new FXMLLoader(getClass().getResource("Layout2.fxml"));

            // Set this class as the controller
            loader.setController(this);

            // Load the scene
            thisStage.setScene(new Scene(loader.load()));

            // Setup the window/stage
            thisStage.setTitle("Passing Controllers Example - Layout2");

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    /**
     * Show the stage that was loaded in the constructor
     */
    public void showStage() {
        thisStage.showAndWait();
    }

    @FXML
    private void initialize() {

        // Set the label to whatever the text entered on Layout1 is
        lblFromController1.setText(controller1.getEnteredText());

        // Set the action for the button
        btnSetLayout1Text.setOnAction(event -> setTextOnLayout1());
    }

    /**
     * Calls the "setTextFromController2()" method on the first controller to update its Label
     */
    private void setTextOnLayout1() {
        controller1.setTextFromController2(txtToFirstController.getText());
    }

}

Layout1.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="This is Layout1!"/>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToSecondController"/>
            <Button fx:id="btnOpenLayout2" mnemonicParsing="false" text="Open Layout2"/>
        </HBox>
        <VBox alignment="CENTER">
            <Label text="Text From Controller2:"/>
            <Label fx:id="lblFromController2" text="Nothing Yet!"/>
        </VBox>
    </VBox>
</AnchorPane>

Layout2.fxml:

<?xml version="1.0" encoding="UTF-8"?>
<?import javafx.geometry.Insets?>
<?import javafx.scene.control.*?>
<?import javafx.scene.layout.AnchorPane?>
<?import javafx.scene.layout.HBox?>
<?import javafx.scene.layout.VBox?>
<AnchorPane xmlns="http://javafx.com/javafx/9.0.1" xmlns:fx="http://javafx.com/fxml/1">
    <VBox alignment="CENTER" spacing="10.0">
        <padding>
            <Insets bottom="10.0" left="10.0" right="10.0" top="10.0"/>
        </padding>
        <Label style="-fx-font-weight: bold;" text="Welcome to Layout 2!"/>
        <VBox alignment="CENTER">
            <Label text="Text From Controller1:"/>
            <Label fx:id="lblFromController1" text="Nothing Yet!"/>
        </VBox>
        <HBox alignment="CENTER_LEFT" spacing="10.0">
            <Label text="Enter Text:"/>
            <TextField fx:id="txtToFirstController"/>
            <Button fx:id="btnSetLayout1Text" mnemonicParsing="false" text="Set Text on Layout1"/>
        </HBox>
    </VBox>
</AnchorPane>
37
задан BoltClock 15 July 2011 в 23:51
поделиться

6 ответов

Вопрос интересен тем, что он показывает детали системы типа .NET. Подобно типам значений, типам строк и делегатов, типы массивов получают специальное обращение в .NET. Самое примечательное странное поведение заключается в том, что вы никогда явно не объявляете тип массива. Компилятор позаботится об этом для вас с достаточной помощью дрожания. System.Array - абстрактный тип, вы будете получать выделенные типы массивов в процессе написания кода. Либо явно создавая тип [], либо используя общие классы, которые имеют массив в своей базовой реализации.

В довольно большой программе, имеющей сотни типов массивов, не является необычным. Это нормально, но для каждого типа есть накладные расходы. Это хранилище требуется только для типа, а не для его объектов. Самый большой кусок - это так называемая «таблица методов». В двух словах, это список указателей на каждый метод экземпляра типа. Как загрузчик классов, так и джиттер работают вместе, чтобы заполнить эту таблицу. Это обычно называется «v-table», но не совсем соответствует, таблица содержит указатели на методы, которые являются не виртуальными и виртуальными.

Вы можете видеть, где это ведет, возможно, дизайнеры были обеспокоены наличием большого количества типов с большими таблицами методов. Итак, искали способы сократить накладные расходы.

Array.Sort () была очевидной целью.

Эта же проблема не относится к родовым типам. Большая разновидность дженериков, одна из многих, одна таблица методов может обрабатывать указатели метода для любого параметра типа ссылочного типа.

30
ответ дан Hans Passant 18 August 2018 в 23:26
поделиться
  • 1
    Я никогда не видел, чтобы кто-то использовал слово «дрожание», чтобы описать что-нибудь кроме утреннего кофеина. Вы являетесь существительным в сокращении JIT в JITer (компилятор «точно вовремя»)? – mattmc3 16 July 2011 в 01:20
  • 2
    Я не совсем уверен. Не виртуальные методы не занимают места в таблицах методов (они используются только для поддержки полиморфной отправки), поэтому переход на статический метод не требуется для сохранения памяти. Плюс, мы говорим о (в худшем случае) нескольких килобайтах памяти - если это было проблемой, они никогда бы не реализовали сборку мусора. – Bevan 16 July 2011 в 01:44
  • 3
    @Bevan - Kudos за то, что заметили несоответствие, но не виртуальные методы действительно используют слот в таблице методов. Это часто цитируемая разница между C # и Java, но на самом деле это не так. Их даже вызывают с помощью виртуального вызова, задокументированного в этом сообщении в блоге: blogs.msdn.com/b/ericgu/archive/2008/07/02/… Кроме того, таблица не существует в памяти GC, он хранится в куче загрузчика. Он может быть освобожден только путем разгрузки appdomain. – Hans Passant 16 July 2011 в 08:55
  • 4
    @Hans - Интересный пост, спасибо за ссылку. Что касается GC, моя точка зрения заключалась не в том, что таблица методов была подчинена GC, но в том, что среда выполнения вообще не использовала GC, если бы вызывало беспокойство несколько килобайт дополнительных метаданных. – Bevan 16 July 2011 в 09:44
3
ответ дан Bevan 18 August 2018 в 23:26
поделиться

Вы сравниваете два разных типа «контейнеров объектов»:

MyList представляет собой общий набор типа List , класс оболочки типа int, где List<T> представляет собой строго типизированный список объектов. Сам класс List предоставляет методы для поиска, сортировки и управления содержащимися в нем объектами.

MyArray является базовой структурой данных типа Array . Массив не предоставляет такой же богатый набор методов, как List. Массивы могут одновременно быть одномерными, многомерными или зубчатыми, в то время как списки из коробки только одномерные.

Взгляните на этот вопрос, он дает более богатое обсуждение этих типов данных: Array versus List<T>: When to use which?

5
ответ дан Community 18 August 2018 в 23:26
поделиться
  • 1
    +1: Сортировка массива является более сложным делом, чем сортировка списка, поэтому Array.Sort имеет перегрузки 17 , тогда как List<T>.Sort имеет только четыре. – StriplingWarrior 16 July 2011 в 00:32
  • 2
    Сложнее? – Magnus 16 July 2011 в 01:04
  • 3
    @StriplingWarrior: Фактически, сортировка обоих может / будет использовать точно тот же код ... – BlueRaja - Danny Pflughoeft 16 July 2011 в 01:07
  • 4
    @StriplingWarrior List.Sort просто вызывает Array.Sort внутренне – Magnus 16 July 2011 в 01:11
  • 5
    – Random832 16 July 2011 в 05:41

Одна из причин может быть вызвана тем, что Array.Sort был разработан в .NET 1.0, у которого не было никаких дженериков.

2
ответ дан John Saunders 18 August 2018 в 23:26
поделиться
  • 1
    Ahhhh ... обратная совместимость. – BoltClock♦ 16 July 2011 в 00:12
  • 2
    Что делать с дженериками? .Net 1.0 имел ArrayList, который также имел функцию Sort()-члена, поэтому этот ответ неверен. – BlueRaja - Danny Pflughoeft 16 July 2011 в 00:15
  • 3
    Нет, это абсолютно верно в отношении Array.Sort. Это не может быть причиной всех решений, связанных с экземплярами - vs-static, связанных с коллекциями. – John Saunders 16 July 2011 в 00:16
  • 4
    Я не могу судить, является ли это причиной или нет в этом случае, но это очень хороший момент и что-то, о чем я бы никогда не подумал. – David Lee 16 July 2011 в 18:20

Вероятно, это связано с наследованием. Класс Array не может быть получен вручную. Но как ни странно, вы можете объявить массив чего угодно и получить экземпляр System.Array, который строго типизирован, даже до того, как дженерики позволили вам иметь строго типизированные коллекции. Массив, по-видимому, является одной из тех магических частей рамки.

Также обратите внимание, что ни один из методов экземпляра, представленных в массиве, не массивно модифицирует массив. Кажется, что SetValue() является единственным, что меняет что-либо. Класс Array предоставляет множество статических методов, которые могут изменять содержимое массива, например, Reverse () и Sort (). Не уверен, что это значимо - может быть, кто-то здесь может дать некоторое представление о том, почему это так.

Напротив, List<T> (чего не было в 1.0 каркасных днях) и таких классов, как ArrayList (который был примерно в то время) - это просто запущенные классы мельниц, не имеющие особого значения в рамках. Они предоставляют общий метод экземпляра .Sort (), так что, когда вы унаследовали от этих классов, вы получили бы эту функциональность или могли бы переопределить ее.

Однако эти методы сортировки вышли из моды в любом случае поскольку методы расширения, такие как сортировка стиля Linq's .OrderBy (), стали следующей эволюцией.

- EDIT -

Другой, более циничный, и вы можете запросить и отсортировать массивы и списки и любой другой перечислимый объект с таким же механизмом. ответ может быть просто - , как это сделала Java , поэтому Microsoft сделала это так же в версии версии 1.0, поскольку в то время они были заняты игрой в догонялки.

3
ответ дан mattmc3 18 August 2018 в 23:26
поделиться

Я не уверен, но я думаю, может быть, просто так, чтобы массивы были как можно ближе к примитивам.

0
ответ дан Paulpro 18 August 2018 в 23:26
поделиться