Существует несколько различных альтернативных подходов, которые могут быть использованы, однако это зависит от того, чем вы владеете, и может отличаться от того, чем вы не владеете, и не может изменять или изменять. Реальная реализация может зависеть от того, как часто 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
должна измениться, вещи, вероятно, сломаются. Хуже всего то, что они могут сломаться без указания причины и основной причины.
Это потому, что Точка
является типом значения ( struct
).
Из-за этого при доступе к Origin
, вы обращаетесь к копии значения, хранящегося в классе, а не к самому значению, как для ссылочного типа ( class
), поэтому, если вы установите X
на нем, то вы устанавливаете свойство на копии, а затем отбрасываете его, оставляя исходное значение без изменений. Вероятно, это не то, что вы намеревались сделать, поэтому компилятор предупреждает вас об этом.
Если вы хотите изменить только значение X
, вам нужно сделать что-то вроде этого:
Origin = new Point(10, Origin.Y);
Просто удалите свойство, "установлены" следующим образом, и затем все работает как всегда.
В случае типов примитивов 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]
}
}
Проблема в том, что вы указываете значение, находящееся в стеке, и значение не будет возвращено обратно в свойство orignal, поэтому C # не позволяет вы должны вернуть ссылку на тип значения. Я думаю, вы можете решить эту проблему, удалив свойство Origin и вместо этого используя публичное поле, да, я знаю, что это не очень хорошее решение. Другое решение - не использовать Point, а вместо этого создать собственный тип Point как объект.
Использование резервной переменной не поможет. Тип Point
- это тип значения.
Вам необходимо присвоить все значение Point свойству Origin: -
Origin = new Point(10, Origin.Y);
Проблема в том, что при доступе к свойству Origin то, что возвращается get
- это копия структуры Point в автоматически созданном поле свойств Origin. Следовательно, ваша модификация поля X этой копии не повлияет на базовое поле. Компилятор обнаруживает это и выдает ошибку, поскольку эта операция совершенно бесполезна.
Даже если бы вы использовали свою собственную поддерживающую переменную, ваше get
будет выглядеть так: -
get { return myOrigin; }
Вы все равно вернете копию структуры Point, и вы получите ту же ошибку.
Хм ...
Я полагаю, что загвоздка здесь в том, что вы пытаетесь назначить вспомогательные значения объекта в операторе, а не назначать сам объект. В этом случае вам нужно назначить объект Point целиком, поскольку тип свойства - Point.
Point newOrigin = new Point(10, 10);
Origin = newOrigin;
Надеюсь, я понял это