Ошибка: “Не может изменить возвращаемое значение” c#

Существует несколько различных альтернативных подходов, которые могут быть использованы, однако это зависит от того, чем вы владеете, и может отличаться от того, чем вы не владеете, и не может изменять или изменять. Реальная реализация может зависеть от того, как часто structA используется с API по сравнению с structB. Вы действительно не предоставляете достаточно информации для окончательного предлагаемого подхода.

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

Есть две структуры, structA и structB, которые имеют общих членов. Эти общие члены хранят тот же тип данных, и при проверке вся structB содержится в structA, как в:

typedef struct {
    int a;
    int b;      // beginning of portion that is same as structB below.
    int c;
    int d;      // end of portion that is same as structB below.
    int e;
} structA;

typedef struct {
    int b;       // same type of data as in b member of structA above
    int c;       // same type of data as in c member of structA above
    int d;       // same type of data as in d member of structA above
} structB;

Для конкретного примера, structA описывает объект в некотором трехмерном пространство, местоположение которого является кортежем x, y, z, который указан в structA членах b, c, d и structB, используется для хранения местоположения только как x, y, z кортеж. [одна тысяча сто восемьдесят-два]

У вас есть API, который работает с данными в structB, и, поскольку те же данные находятся в structA, вы столкнулись с проблемой, что у вас должен быть API, состоящий из набора функций, который дублируется, один версия API, которая принимает в качестве аргумента structB, а другая принимает в качестве аргумента structA.

Чтобы расширить наш конкретный пример объектов в трехмерном пространстве, у вас есть API, который содержит функцию translate(), которая переводит координаты на некоторое расстояние. Поскольку у вас есть две разные структуры в соответствии с MISRA C, вам понадобятся две разные версии этой функции: translate_structA(), которая принимает в качестве аргумента a structA, и вторую translate_structB(), которая принимает в качестве аргумента a structB. ].

Итак, вы столкнулись с необходимостью написать две версии каждой функции в вашем API, и вы не хотите этого делать.

Альтернатива 1 - заменить клонированные элементы фактической структурой

Использовать хорошую разработку программного обеспечения и вместо использования этого типа данных structB в structA в качестве клонированного набора элементов, вместо этого замените этих клонированных членов на structB.

typedef struct {
    int b;       // same type of data as in b member of structA above
    int c;       // same type of data as in c member of structA above
    int d;       // same type of data as in d member of structA above
} structB;

typedef struct {
    int a;
    structB xyz;    // replace the cloned members with an actual structB
    int e;
} structA;

Затем вы пишете API, который работает с structB только в терминах structB. В тех местах, где вы используете structA, вы просто используете член xyz в интерфейсе вызова функции.

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

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

Примечание о следующих двух альтернативах

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

Если зависимость structA от structB не указана как вид контракта с использованием structB в structA, вы вводите своего рода логическое или когнитивное межмодульное соединение , в котором у вас есть общий компонент, который представляет собой сам исходный код, а не программный компонент, полученный из исходного кода.

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

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

Альтернатива 2 - сортировка в / из интерфейсного объекта

Если у вас нет контроля над структурами, то другой альтернативой является выполнение сортировки данных в structA и из него в a structB, а затем написать API только в терминах structB. Тогда в любом месте, где structA нужно использовать API, вы должны иметь маршаллинг или преобразование, в котором выбираются конкретные данные в structA для создания временного structB, который затем используется с функцией. Если функция изменяет данные в structB, вам нужно будет скопировать данные из structB обратно в structA, прежде чем удалять временные.

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

Есть несколько способов сделать этот подход маршаллинга, в основном определяемый тем, будут ли интерфейсы API возвращать измененный объект данных или нет.

Во-первых, должен иметь дублированный набор функций, которые вызываются с использованием structA, и этот дублирующий набор функций обрабатывает маршалинг данных между временным structB, который затем используется при вызове фактического API, который занимает structB.

Так что-то вроде:

int funcThing (structB thing);

int funcThing_structA (structA thing) {
    structB temp = {0};
    temp.b = thing.b;
    temp.c = thing.c;
    temp.d = thing.d;
    return funcThing (temp);
}

Альтернативой вышеперечисленному может быть что-то вроде:

int funcThing1 (structB thing);
int funcThing2 (structB thing);
int funcThing3 (structB thing);

int funcThingSet_structA (structA thing, int (*f)(structB thing)) {
    structB temp = {0};
    temp.b = thing.b;
    temp.c = thing.c;
    temp.d = thing.d;
    return f (temp);
}

// and the above is used like
structA thingA;
//  …  code
i = funcThingSet_structA (thingA, funcThing1);  // call funcThing1() with the structA data
i = funcThingSet_structA (thingA, funcThing2);  // call funcThing2() with the structA data

i = funcThingSet_structA (thingA, funcThing3);  // call funcThing3() with the structA data

Если функции могут изменять данные, то вам нужно убедиться, что structA обновляется как в:

int funcThing1 (structB *thing);
int funcThing2 (structB *thing);
int funcThing3 (structB *thing);

int funcThingSet_structA (structA *thing, int (*f)(structB *thing)) {
    structB temp = {0};
    int iRetVal = 0;

    temp.b = thing->b;
    temp.c = thing->c;
    temp.d = thing->d;

    iRetVal = f (&temp);

    thing->b = temp.b;
    thing->c = temp.c;
    thing->d = temp.d;
    return iRetVal;
}

// and the above is used like
structA thingA;
//  …  code
i = funcThingSet_structA (&thingA, funcThing1);  // call funcThing1() with the structA data
i = funcThingSet_structA (&thingA, funcThing2);  // call funcThing2() with the structA data

i = funcThingSet_structA (&thingA, funcThing3);  // call funcThing3() with the structA data

Вы также можете иметь API в терминах structB и использовать вспомогательные функции интерфейса, такие как:

structB *AssignAtoB (structB *pB, structA A) {
    pB->b = A.b;
    pB->c = A.c;
    pB->d = A.d;
    return pB;
}

structB ConvertAtoB (structA A) {
    structB B = {0};
    B.b = A.b;
    B.c = A.c;
    B.d = A.d;
    return B;
}

void AssignBtoA (structA *pA, structB B) {
    pA->b = B.b;
    pA->c = B.c;
    pA->d = B.d;
}

Тогда вы можете сделать что-то вроде:

int funcThing1 (structB thing);
int funcThing2 (structB thing);
int funcThing3 (structB thing);


structA aThing;
//  …. code
{  // create a local scope for this temporary bThing.
    structB bThing = ConvertAtoB (aThing);
    i = funcThing1(bThing);
    // other operations on bThing and then finally.
    AssignBtoA (&aThing, bThing);
}

Или ваши функции API могут возвращать structB, в этом случае вы можете сделать что-то вроде:

structB funcThing1 (structB thing);
structB funcThing2 (structB thing);
structB funcThing3 (structB thing);

structA aThing;
//  …. code
{  // create a local scope for this temporary bThing.
    structB bThing = ConvertAtoB (aThing);
    bThing = funcThing1(bThing);
    bThing = funcThing2(bThing);
    AssignBtoA (&aThing, bThing);
}

или

{  // create a local scope for this temporary bThing.
    structB bThing = ConvertAtoB (aThing);
    AssignBtoA (&aThing, funcThing2(funcThing1(bThing)));
}

или даже просто

AssignBtoA (&aThing, funcThing2(funcThing1(ConvertAtoB (aThing))))

Альтернатива 3 - хрупкое использование указателей

Другой альтернативой является создание указателя, адрес которого начинается с части structB в [ 1172]. Хотя я только смутно знаком с MISRA, я почти не сомневаюсь, что этот подход противоречит правилам, поскольку он в значительной степени мерзок. Тем не менее, здесь все равно, как я видел, это было сделано в старом коде, написанном людьми без надлежащей подготовки специалистов по разработке программного обеспечения.

Используя две вышеупомянутые структуры, создайте вспомогательную функцию или макрос, который сгенерирует указатель на смещение в structA, где начинаются данные structB. Например:

structB MakeClone (structA thing) {
    return *(structB *)&thing.b;   // return a copy of the structB part of structA
}

или

structB *MakePointer (structA *pThing) {
    return (structB *)&thing.b;    // return a pointer to the structB part of structA
}

Макрос препроцессора также может использоваться для генерации указателя второго случая, как в:

#define MAKEPOINTER(pThing) ((structB *)&((pThing)->b))

I также видели, где вместо использования вспомогательной функции с присваиванием, как в:

int funcThing (structB *pBthing);

//  then in code want to use the above function with a structA
structA  aThing = {0};

// do things with aThing then call our function that wants a structB

funcThing (MAKEPOINTER(&aThing));

вместо этого они просто жестко закодируют указатель, в результате чего действительно трудно найти, где это было сделано во время обслуживания: [ 11115]

funcThing ((structB *)&(aThing.b));

Я также видел подход указателя, используемый с присваиванием, используя memcpy() для выполнения присваивания. Поэтому, если у нас есть код, подобный следующему:

structA aThing = {0};
structB bThing = {0};

// somewhere in code we have
memcpy (&bThing, &aThing.b, sizeof(structB));  // assign the structB part of aThing to a structB
// more code to modify bThing then call our function
funcThing (&bThing);
memcpy (&aThing.b, &bThing, sizeof(structB));  // assign the structB back into the structB part of aThing

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

135
задан DaveInCaz 6 September 2019 в 18:22
поделиться

5 ответов

Это потому, что Точка является типом значения ( struct ).

Из-за этого при доступе к Origin , вы обращаетесь к копии значения, хранящегося в классе, а не к самому значению, как для ссылочного типа ( class ), поэтому, если вы установите X на нем, то вы устанавливаете свойство на копии, а затем отбрасываете его, оставляя исходное значение без изменений. Вероятно, это не то, что вы намеревались сделать, поэтому компилятор предупреждает вас об этом.

Если вы хотите изменить только значение X , вам нужно сделать что-то вроде этого:

Origin = new Point(10, Origin.Y);
176
ответ дан 23 November 2019 в 23:48
поделиться

Просто удалите свойство, "установлены" следующим образом, и затем все работает как всегда.

В случае типов примитивов instread используют получение; набор;...

using Microsoft.Xna.Framework;
using System;

namespace DL
{
    [Serializable()]
    public class CameraProperty
    {
        #region [READONLY PROPERTIES]
        public static readonly string CameraPropertyVersion = "v1.00";
        #endregion [READONLY PROPERTIES]


        /// <summary>
        /// CONSTRUCTOR
        /// </summary>
        public CameraProperty() {
            // INIT
            Scrolling               = 0f;
            CameraPos               = new Vector2(0f, 0f);
        }
        #region [PROPERTIES]   

        /// <summary>
        /// Scrolling
        /// </summary>
        public float Scrolling { get; set; }

        /// <summary>
        /// Position of the camera
        /// </summary>
        public Vector2 CameraPos;
        // instead of: public Vector2 CameraPos { get; set; }

        #endregion [PROPERTIES]

    }
}      
0
ответ дан 23 November 2019 в 23:48
поделиться

Проблема в том, что вы указываете значение, находящееся в стеке, и значение не будет возвращено обратно в свойство orignal, поэтому C # не позволяет вы должны вернуть ссылку на тип значения. Я думаю, вы можете решить эту проблему, удалив свойство Origin и вместо этого используя публичное поле, да, я знаю, что это не очень хорошее решение. Другое решение - не использовать Point, а вместо этого создать собственный тип Point как объект.

0
ответ дан 23 November 2019 в 23:48
поделиться

Использование резервной переменной не поможет. Тип Point - это тип значения.

Вам необходимо присвоить все значение Point свойству Origin: -

Origin = new Point(10, Origin.Y);

Проблема в том, что при доступе к свойству Origin то, что возвращается get - это копия структуры Point в автоматически созданном поле свойств Origin. Следовательно, ваша модификация поля X этой копии не повлияет на базовое поле. Компилятор обнаруживает это и выдает ошибку, поскольку эта операция совершенно бесполезна.

Даже если бы вы использовали свою собственную поддерживающую переменную, ваше get будет выглядеть так: -

get { return myOrigin; }

Вы все равно вернете копию структуры Point, и вы получите ту же ошибку.

Хм ...

8
ответ дан 23 November 2019 в 23:48
поделиться

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

Point newOrigin = new Point(10, 10);
Origin = newOrigin;

Надеюсь, я понял это

0
ответ дан 23 November 2019 в 23:48
поделиться
Другие вопросы по тегам:

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