Действительно ли это - четкое использование goto?

Хорошо, я записал это принимающее во внимание, что было сказано в этом потоке. Спасибо за справку, и я надеюсь, что этот сценарий выручит других. У меня нет гарантии для ее использования, поэтому СКОПИРУЙТЕ прежде, чем выполнить ее. Это должно работа со всеми базами данных; и это работало отлично самостоятельно.

РЕДАКТИРОВАНИЕ: Добавленный Вар наверху, для который набор символов/сопоставлять преобразовать в. EDIT2: Изменяет набор символов/сопоставлять базы данных и таблиц по умолчанию

<?php

function MysqlError()
{
    if (mysql_errno())
    {
        echo "<b>Mysql Error: " . mysql_error() . "</b>\n";
    }
}

$username = "root";
$password = "";
$db = "database";
$host = "localhost";

$target_charset = "utf8";
$target_collate = "utf8_general_ci";

echo "<pre>";

$conn = mysql_connect($host, $username, $password);
mysql_select_db($db, $conn);

$tabs = array();
$res = mysql_query("SHOW TABLES");
MysqlError();
while (($row = mysql_fetch_row($res)) != null)
{
    $tabs[] = $row[0];
}

// now, fix tables
foreach ($tabs as $tab)
{
    $res = mysql_query("show index from {$tab}");
    MysqlError();
    $indicies = array();

    while (($row = mysql_fetch_array($res)) != null)
    {
        if ($row[2] != "PRIMARY")
        {
            $indicies[] = array("name" => $row[2], "unique" => !($row[1] == "1"), "col" => $row[4]);
            mysql_query("ALTER TABLE {$tab} DROP INDEX {$row[2]}");
            MysqlError();
            echo "Dropped index {$row[2]}. Unique: {$row[1]}\n";
        }
    }

    $res = mysql_query("DESCRIBE {$tab}");
    MysqlError();
    while (($row = mysql_fetch_array($res)) != null)
    {
        $name = $row[0];
        $type = $row[1];
        $set = false;
        if (preg_match("/^varchar\((\d+)\)$/i", $type, $mat))
        {
            $size = $mat[1];
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} VARBINARY({$size})");
            MysqlError();
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} VARCHAR({$size}) CHARACTER SET {$target_charset}");
            MysqlError();
            $set = true;

            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }
        else if (!strcasecmp($type, "CHAR"))
        {
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} BINARY(1)");
            MysqlError();
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} VARCHAR(1) CHARACTER SET {$target_charset}");
            MysqlError();
            $set = true;

            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }
        else if (!strcasecmp($type, "TINYTEXT"))
        {
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} TINYBLOB");
            MysqlError();
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} TINYTEXT CHARACTER SET {$target_charset}");
            MysqlError();
            $set = true;

            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }
        else if (!strcasecmp($type, "MEDIUMTEXT"))
        {
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} MEDIUMBLOB");
            MysqlError();
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} MEDIUMTEXT CHARACTER SET {$target_charset}");
            MysqlError();
            $set = true;

            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }
        else if (!strcasecmp($type, "LONGTEXT"))
        {
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} LONGBLOB");
            MysqlError();
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} LONGTEXT CHARACTER SET {$target_charset}");
            MysqlError();
            $set = true;

            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }
        else if (!strcasecmp($type, "TEXT"))
        {
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} BLOB");
            MysqlError();
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} TEXT CHARACTER SET {$target_charset}");
            MysqlError();
            $set = true;

            echo "Altered field {$name} on {$tab} from type {$type}\n";
        }

        if ($set)
            mysql_query("ALTER TABLE {$tab} MODIFY {$name} COLLATE {$target_collate}");
    }

    // re-build indicies..
    foreach ($indicies as $index)
    {
        if ($index["unique"])
        {
            mysql_query("CREATE UNIQUE INDEX {$index["name"]} ON {$tab} ({$index["col"]})");
            MysqlError();
        }
        else
        {
            mysql_query("CREATE INDEX {$index["name"]} ON {$tab} ({$index["col"]})");
            MysqlError();
        }

        echo "Created index {$index["name"]} on {$tab}. Unique: {$index["unique"]}\n";
    }

    // set default collate
    mysql_query("ALTER TABLE {$tab}  DEFAULT CHARACTER SET {$target_charset} COLLATE {$target_collate}");
}

// set database charset
mysql_query("ALTER DATABASE {$db} DEFAULT CHARACTER SET {$target_charset} COLLATE {$target_collate}");

mysql_close($conn);
echo "</pre>";

?>
7
задан Machavity 12 October 2018 в 17:18
поделиться

7 ответов

Есть ли другой способ, которым обычно восстановиться после таких ошибок, когда вы хочу повторить операцию после обработка исключения?

Да, в вызывающем коде. Позвольте вызывающей стороне этого метода решить, нужно ли им повторить логику или нет.

ОБНОВЛЕНИЕ:

Чтобы уточнить, вы должны перехватывать исключения, только если вы действительно можете их обработать. Ваш код в основном говорит:

"Я понятия не имею, что произошло, но что бы я ни сделал, все взорвите ... так что давайте сделаем это снова. "

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

ОБНОВЛЕНИЕ 2:

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

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

//The main thread might look something like this

try{
    var database = LoadDatabaseFromUserInput();

    //Do other stuff with database
}
catch(Exception ex){
    //Since this is probably the highest layer,
    // then we have no clue what just happened
    Logger.Critical(ex);
    DisplayTheIHaveNoIdeaWhatJustHappenedAndAmGoingToCrashNowMessageToTheUser(ex);
}

//And here is the implementation

public IDatabase LoadDatabaseFromUserInput(){

    IDatabase database = null;
    userHasGivenUpAndQuit = false;

    //Do looping close to the control (in this case the user)
    do{
        try{
            //Wait for user input
            GetUserInput();

            //Check user input for validity
            CheckConfigFile();
            CheckDatabaseConnection();

            //This line shouldn't fail, but if it does we are
            // going to let it bubble up to the next layer because
            // we don't know what just happened
            database = LoadDatabaseFromSettings();
        }
        catch(ConfigFileException ex){
            Logger.Warning(ex);
            DisplayUserFriendlyMessage(ex);
        }
        catch(CouldNotConnectToDatabaseException ex){
            Logger.Warning(ex);
            DisplayUserFriendlyMessage(ex);
        }
        finally{
            //Clean up any resources here
        }
    }while(database != null); 
}

Теперь, очевидно, я понятия не имею, что пытается сделать ваше приложение, и это, безусловно, не производственный пример. Надеюсь, вы получите Общая идея. Перестройте программу, чтобы избежать ненужных перерывов в работе приложения.

Ура, Джош

15
ответ дан 6 December 2019 в 05:55
поделиться

Возможно, я что-то упускаю, но почему вы не можете просто использовать цикл while? это даст вам один и тот же цикл навсегда, если у вас есть функция исключения (что является плохим кодом), которую дает ваш код.

IDatabase database = null;

while(database == null){
   try
   {
        database = databaseLoader.LoadDatabase();
   }
   catch(Exception e)
   {
        var connector = _userInteractor.GetDatabaseConnector();
        if(connector == null)
                throw new ConfigException("Could not load the database specified in your config file.");
        databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
        //just in case??
        database = null;
   }
 }

если вам нужно использовать goto в обычном коде, вам не хватает логического потока. которые вы можете получить, используя стандартные конструкции, if, while, и т. д.

7
ответ дан 6 December 2019 в 05:55
поделиться

Лично я бы использовал это в отдельном методе, который возвращает код состояния успеха или неудачи. Затем в коде, который будет вызывать этот метод, у меня может быть какое-то магическое количество раз, когда я буду продолжать попытки, пока код состояния не станет «Успех». Мне просто не нравится использовать try / catch для потока управления.

4
ответ дан 6 December 2019 в 05:55
поделиться

Понятно? На самом деле, нет. Я думаю, что вы действительно хотите сначала загрузить базу данных, а затем, если это не сработало, попробовать загрузить ее другим способом. Это правильно? Давайте так и напишем код.

IDatabase loadedDatabase = null;

// first try
try
{
    loadedDatabase = databaseLoader.LoadDatabase();
}
catch(Exception e) { }  // THIS IS BAD DON'T DO THIS

// second try
if(loadedDatabase == null) 
{
    var connector = _userInteractor.GetDatabaseConnector();
    if(connector == null)
        throw new ConfigException("Could not load the database specified in your config file.");
    databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    loadedDatabase = databaseLoader.LoadDatabase()
}

Это более ясно показывает, что вы на самом деле делаете. В качестве дополнительного бонуса другие программисты не выколют вам глаза. :)

ПРИМЕЧАНИЕ: вы почти наверняка не хотите перехватывать исключение. Вероятно, есть более конкретное исключение, которое вы бы предпочли поймать. Это также приведет к перехвату TheComputerIsOnFireException, после чего повторять попытку не стоит.

s напишите код таким образом.

IDatabase loadedDatabase = null;

// first try
try
{
    loadedDatabase = databaseLoader.LoadDatabase();
}
catch(Exception e) { }  // THIS IS BAD DON'T DO THIS

// second try
if(loadedDatabase == null) 
{
    var connector = _userInteractor.GetDatabaseConnector();
    if(connector == null)
        throw new ConfigException("Could not load the database specified in your config file.");
    databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    loadedDatabase = databaseLoader.LoadDatabase()
}

Это более ясно показывает, что вы на самом деле делаете. В качестве дополнительного бонуса другие программисты не выколачивают вам глаза. :)

ПРИМЕЧАНИЕ: вы почти наверняка не хотите перехватывать Exception. Вероятно, есть более конкретное исключение, которое вы бы предпочли уловить. Это также приведет к перехвату TheComputerIsOnFireException, после чего повторять попытку не стоит.

s напишите код таким образом.

IDatabase loadedDatabase = null;

// first try
try
{
    loadedDatabase = databaseLoader.LoadDatabase();
}
catch(Exception e) { }  // THIS IS BAD DON'T DO THIS

// second try
if(loadedDatabase == null) 
{
    var connector = _userInteractor.GetDatabaseConnector();
    if(connector == null)
        throw new ConfigException("Could not load the database specified in your config file.");
    databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    loadedDatabase = databaseLoader.LoadDatabase()
}

Это более ясно показывает, что вы на самом деле делаете. В качестве дополнительного бонуса другие программисты не выколачивают вам глаза. :)

ПРИМЕЧАНИЕ: вы почти наверняка не хотите перехватывать исключение. Вероятно, есть более конкретное исключение, которое вы бы предпочли поймать. Это также приведет к обнаружению исключения TheComputerIsOnFireException, после чего повторять попытку не стоит.

2
ответ дан 6 December 2019 в 05:55
поделиться

Нет, это не нормально: http://xkcd.com/292/

1
ответ дан 6 December 2019 в 05:55
поделиться

Кстати, я думаю, что существует вероятность бесконечного цикла, если вы всегда получаете исключение.

Технически в вашей структуре goto нет ничего плохого, но я бы предпочел вместо этого использовать цикл while. Что-то вроде:

IDatabase database = null;

bool bSuccess = false;
int iTries = 0
while (!bSuccess) // or while (database == null)
{
    try
    {
        iTries++;
        database = databaseLoader.LoadDatabase();
        bSuccess = true;
    }
    catch(DatabaseLoaderException e)
    {
        //Avoid an endless loop
        if (iTries > 10)
             throw e;

        var connector = _userInteractor.GetDatabaseConnector();
        if(connector == null)
             throw new ConfigException("Could not load the database specified in your config file.");
        databaseLoader = DatabaseLoaderFacade.GetDatabaseLoader(connector);
    }
}
1
ответ дан 6 December 2019 в 05:55
поделиться

Obligatory XKCD

1
ответ дан 6 December 2019 в 05:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: