Подтвердить что ты не робот

Многократная инициализация в цикле С# 'for'

Как я могу (если вообще возможно) инициализировать несколько переменных различного типа в цикле С# for? Пример:

for (MyClass i = 0, int j = 1; j<3; j++,i++)
4b9b3361

Ответ 1

Это невозможно. Поместите одно из объявлений перед циклом:

MyClass i = 0;
for (int j = 1; j < 3; j++, i++)

Или для симметрии, оба из них:

MyClass i = 0;
int j = 1;
for (; j < 3; j++, i++)

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

MyClass i = 0;
for (int j = 0; j < 3; j++)
{
    ...
    i++;
}

Обратите внимание, что если i и j имеют один и тот же тип, вы можете объявить их как в for-loop:

for (int i = 0, j = 1; j < 3; j++, i++)

Ответ 2

Конечно, это можно сделать. Просто используйте ключевое слово dynamic:

public static void Main(string[] args) {
    for (dynamic x = 0, y = new MyClass { a = 20, b = 30 }; x < 100; x++, y.a++, y.b--) {
        Console.Write("X=" + x + " (" + x.GetType() + "\n" +
                      "Y.a=" + y.a + ",Y.b=" + y.b + " (" + y.GetType() + "\n");
     }
}

class MyClass {
    public int a = 0, b = 0;
}

Проведите отличный день!

Ответ 3

Да, это можно сделать. Вы можете инициализировать переменные разных типов внутри оператора for, но вы не можете объявлять переменные разных типов внутри оператора for. Чтобы инициализировать переменные разных типов внутри оператора for, вы должны объявить все типы перед циклом for. Например:

int xx;
string yy;
for(xx=0, yy=""; xx<10; xx++)
    {
    ....
    }

[EDIT] Добавление дополнительной информации для полноты. Это выходит за рамки того, что запросил ОП, но может быть полезным для других. Просто инициализировать переменные того же типа в цикле for, просто отделяйте инициализацию запятыми. Вы также можете изменить несколько переменных в третьем разделе. Вы не можете иметь несколько разделенных запятыми разделов во втором разделе сравнения, но вы можете использовать && || а также! для создания сложного булевого сечения, основанного на нескольких переменных.

for(int i=0, j=0, k=99; i<10 && k<200; i++, j++, k += 2)

Однако, это не очень хорошая практика, чтобы сделать оператор for настолько сложным, что трудно понять, что происходит.

Ответ 4

Это вредно?

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

На самом фундаментальном уровне анализатор языка различает объявления, выражения и выражения. Языки курчавых скобок затуманивают это различие, вы можете превратить любое выражение в утверждение, просто положив после него точку с запятой. И в некоторых случаях принять декларацию внутри оператора, утверждение for (;;) является хорошим примером. В большинстве случаев этот синтаксис вполне приемлем на языках C или С++:

int x = 42;
x;

Это не совсем хорошо, это бессмысленный код. Язык С# поднял планку на этом, он отвергнет это. Но не:

int x = 42;
x++;

Для принятия этого правила парсер синтаксиса добавляется специальное правило.

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

Ответ 5

Я обычно помещаю объявления перед циклом и использую дополнительные фигурные скобки для ограничения объема объявлений:

{ //limit the scope: i, count, iDivisibleBy2, iDivisibleBy3, iDivisibleBy5
    int i = 0, count = 100;
    bool iDivisibleBy2 = true, iDivisibleBy3 = true, iDivisibleBy5 = true;
    for( ; i < count; ++i, iDivisibleBy2 = (i % 2 == 0), iDivisibleBy3 = ( i % 3 == 0 ), iDivisibleBy5 = ( i % 5 == 0 ) )
    {
        //...
    }
}

Ответ 6

for (initializer; condition; iterator)
{
body
}

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

1) Объявление и инициализация локальной переменной цикла. Переменная локальна для цикла и не может быть доступна извне цикла.

2) Нулевые или более выражения выражения из следующего списка, разделенные запятыми.

    assignment statement

    invocation of a method

    prefix or postfix increment expression, such as ++i or i++

    prefix or postfix decrement expression, such as --i or i--

    creation of an object by using new

    await expression

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

http://msdn.microsoft.com/en-us/library/ch45axte.aspx

Ответ 7

Я не думаю, что вы можете определить более одного типа внутри цикла for. только для (int я = 0, j = 3; j < 7; j ++, я ++)

Ответ 8

Вы не можете определить более одной переменной в структуре цикла. Попробуйте использовать код ниже:

Вариант 1: Одна переменная, объявленная перед циклом, и вручную увеличивающая цикл за один раз на итерацию.

MyClass i = 0;
for (int j = 1; j<3; j++)
{
  //do stuff
  i++
}

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

MyClass i = 0;
int j = 1
for (; j<3; j++)
{
  //do stuff
  i++
}

Вариант 3: обе переменные устанавливаются ранее для структуры цикла, и обе переменные увеличиваются в цикле, оставляя цикл только для проверки состояния, которое в этот момент вы могли бы просто сделать while.

MyClass i = 0;
int j = 1
for (; j<3)
{
  //do stuff
  j++
  i++
}

Вариант 4: писать как цикл while

MyClass i = 0;
int j = 1
while (j<3)
{
  //do stuff
  j++
  i++
}

Ответ 9

Это не особенно мой опыт, но вот мой мозговой штурм по теме:

В программирующей теории языков синтаксис языка должен быть определен без двусмысленности. Я не могу подробно рассказать об этом, поскольку прошло несколько лет с тех пор, как я изучил эти темы. Но вы можете проверить форму Backus-Naur, которая представляет собой нотацию, описывающую "грамматику" языка. И это единственное, с чем я знаком.

Итак, это описание используется при разборе кода. И ваш парсер должен уметь связывать каждую часть вашего кода с "правилами" грамматики. Вы можете увидеть С# Grammar здесь. Это несколько похоже на аналогичную форму.

(1) Взгляните на синтаксис for-statement

for-statement:
    for(for-initializer;for-condition;for-iterator)   
        embedded-statement

а затем синтаксис for-initializer

for-initializer:
    local-variable-declaration
    statement-expression-list

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

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

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

(2) То, что мы наблюдали в 1, - это то, что мы можем поставить для инициализационной части цикла for. И мы знаем, почему ваше предложение не работает. Чтобы стать охотником за головами, проанализируйте причину выбора этого дизайна.

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

Если вы собираетесь добавить несколько операторов объявления, вы можете иметь что-то вроде объявления-списка. И то, что вы собираетесь использовать для seperator, вы, вероятно, не захотите использовать; потому что точка с запятой используется для разделения частей цикла for. Таким образом, вы всегда можете использовать запятую, но если вы используете запятую, тогда правило правила объявления может использоваться только для циклов for, поскольку было бы путать с объявлениями, разделенными запятыми, по всему вашему коду.

Во-вторых, что с этим не так? Если вы спросите меня, я тоже не вижу ничего плохого. Если они разработали язык, на котором он должен был работать. (Я не говорю, что сделанный мной эскиз на 100% правильный, его можно использовать только как отправную точку сеанса мозгового штурма.)

Тогда почему они решили не делать этого? Что побудило их избежать этого?

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

После всех этих и многих других соображений

  • Сколько вы добавляете к удобству использования, удобочитаемости на вашем языке?
  • Или вы даже теряете удобочитаемость?
  • Считаете ли вы, насколько часто программистам нужны эти типы для петли?
  • Даже если они действительно нуждаются в этом, вы должны быть защищены или обескураживая их для такого типа кодирования?

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

Ответ 10

Существует мало причин не иметь альтернативного индексатора, инициализированного внутриполосным. Это не позволяет очистить среду от мусорных заданий var.

for (int x=0,y = 0; x < 100; x++)
{
    if (true) {  y++; }
    // ... use y as a conditional indexer
    // ... x is always the loop indexer
    // ... no overflows
}

Ответ 11

Давайте немного повеселимся. Я оставлю это для вас, чтобы решить, следует ли это использовать в любом месте...: P

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

for(var i = new I1<MyClass>(0, 1); i < 3; i++, i.a++) {
    MyClass myClass = i.a;
}

Перегруженные операторы означают, что вы можете использовать "i" как int во всем мире. Для чистого синтаксиса инициализируйте с помощью 0:

for(I1<float> i = 0; i < array.Length; i++) {
    i.a += array[i]; // accumulate a float value
}

Несколько глупых примеров:

// Three variables
for(I3<object, string, int> i = 0; i < 100; i++) {
    i.a = new object();
    i.b = "This is index " + i;
    i.c = 100 - i;
}

// A class
for(var i = new I1<SomeClass>(0, new SomeClass()); i < 20; i += 2) {
    i.a.someVar1 = "We can have even more variables in here! Woot!";
    i.a.DoSomething(i);
}

// An array
for(var i = new I1<string[]>(0, new[] { "Hi", "Mom" }); i < 10; i++) {
    for(int j = 0; j < i.a.Length; j++) {
        Log(i.a[j]);
    }
}

Вот структуры. Они работают, но не прошли тщательную проверку, поэтому могут быть ошибки:

public struct I1<T> {

    public int index;
    public T a;

    public I1(int index) {
        this.index = index;
        this.a = default(T);
    }
    public I1(int index, T a) {
        this.index = index;
        this.a = a;
    }

    public override bool Equals(object obj) {
        if(!(obj is I1<T>)) return false;
        I1<T> other = (I1<T>)obj;
        return index == other.index && EqualityComparer<T>.Default.Equals(a, other.a);
    }

    public override int GetHashCode() {
        int hash = 17;
        hash = hash * 29 + index.GetHashCode();
        if(typeof(T).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
        return hash;
    }

    public override string ToString() {
        return index.ToString();
    }

    public static implicit operator I1<T>(int other) {
        return new I1<T>(other);
    }

    public static implicit operator int(I1<T> other) {
        return other.index;
    }

    // Unary operators

    public static int operator +(I1<T> a) {
        return +a.index;
    }

    public static int operator -(I1<T> a) {
        return -a.index;
    }

    public static int operator ~(I1<T> a) {
        return ~a.index;
    }

    public static I1<T> operator ++(I1<T> a) {
        a.index++;
        return a;
    }

    public static I1<T> operator --(I1<T> a) {
        a.index--;
        return a;
    }

    // Binary operators

    public static I1<T> operator +(I1<T> a, int b) {
        a.index += b;
        return a;
    }
    public static I1<T> operator +(int a, I1<T> b) {
        b.index += a;
        return b;
    }

    public static I1<T> operator -(I1<T> a, int b) {
        a.index -= b;
        return a;
    }
    public static I1<T> operator -(int a, I1<T> b) {
        b.index = a - b.index;
        return b;
    }

    public static I1<T> operator *(I1<T> a, int b) {
        a.index *= b;
        return a;
    }
    public static I1<T> operator *(int a, I1<T> b) {
        b.index *= a;
        return b;
    }

    public static I1<T> operator /(I1<T> a, int b) {
        a.index /= b;
        return a;
    }
    public static I1<T> operator /(int a, I1<T> b) {
        b.index = a / b.index;
        return b;
    }

    public static I1<T> operator %(I1<T> a, int b) {
        a.index %= b;
        return a;
    }
    public static I1<T> operator %(int a, I1<T> b) {
        b.index = a % b.index;
        return b;
    }

    public static I1<T> operator &(I1<T> a, int b) {
        a.index &= b;
        return a;
    }
    public static I1<T> operator &(int a, I1<T> b) {
        b.index = a & b.index;
        return b;
    }

    public static I1<T> operator |(I1<T> a, int b) {
        a.index |= b;
        return a;
    }
    public static I1<T> operator |(int a, I1<T> b) {
        b.index = a | b.index;
        return b;
    }

    public static I1<T> operator ^(I1<T> a, int b) {
        a.index ^= b;
        return a;
    }
    public static I1<T> operator ^(int a, I1<T> b) {
        b.index = a ^ b.index;
        return b;
    }

    public static I1<T> operator <<(I1<T> a, int b) {
        a.index <<= b;
        return a;
    }

    public static I1<T> operator >>(I1<T> a, int b) {
        a.index >>= b;
        return a;
    }

    // Comparison operators

    public static bool operator ==(I1<T> a, int b) {
        return a.index == b;
    }
    public static bool operator ==(int a, I1<T> b) {
        return a == b.index;
    }

    public static bool operator !=(I1<T> a, int b) {
        return a.index != b;
    }
    public static bool operator !=(int a, I1<T> b) {
        return a != b.index;
    }

    public static bool operator <(I1<T> a, int b) {
        return a.index < b;
    }
    public static bool operator <(int a, I1<T> b) {
        return a < b.index;
    }

    public static bool operator >(I1<T> a, int b) {
        return a.index > b;
    }
    public static bool operator >(int a, I1<T> b) {
        return a > b.index;
    }

    public static bool operator <=(I1<T> a, int b) {
        return a.index <= b;
    }
    public static bool operator <=(int a, I1<T> b) {
        return a <= b.index;
    }

    public static bool operator >=(I1<T> a, int b) {
        return a.index >= b;
    }
    public static bool operator >=(int a, I1<T> b) {
        return a >= b.index;
    }
}

public struct I2<T1, T2> {

    public int index;
    public T1 a;
    public T2 b;

    public I2(int index) {
        this.index = index;
        this.a = default(T1);
        this.b = default(T2);
    }
    public I2(int index, T1 a) {
        this.index = index;
        this.a = a;
        this.b = default(T2);
    }
    public I2(int index, T1 a, T2 b) {
        this.index = index;
        this.a = a;
        this.b = b;
    }

    public override bool Equals(object obj) {
        if(!(obj is I2<T1, T2>)) return false;
        I2<T1, T2> other = (I2<T1, T2>)obj;
        return index == other.index && EqualityComparer<T1>.Default.Equals(a, other.a) && EqualityComparer<T2>.Default.Equals(b, other.b);
    }

    public override int GetHashCode() {
        int hash = 17;
        hash = hash * 29 + index.GetHashCode();
        if(typeof(T1).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
        if(typeof(T2).IsValueType && !object.ReferenceEquals(b, null)) hash = hash * 29 + b.GetHashCode();
        return hash;
    }

    public override string ToString() {
        return index.ToString();
    }

    public static implicit operator I2<T1, T2>(int other) {
        return new I2<T1, T2>(other);
    }

    public static implicit operator int(I2<T1, T2> other) {
        return other.index;
    }

    // Unary operators

    public static int operator +(I2<T1, T2> a) {
        return +a.index;
    }

    public static int operator -(I2<T1, T2> a) {
        return -a.index;
    }

    public static int operator ~(I2<T1, T2> a) {
        return ~a.index;
    }

    public static I2<T1, T2> operator ++(I2<T1, T2> a) {
        a.index++;
        return a;
    }

    public static I2<T1, T2> operator --(I2<T1, T2> a) {
        a.index--;
        return a;
    }

    // Binary operators

    public static I2<T1, T2> operator +(I2<T1, T2> a, int b) {
        a.index += b;
        return a;
    }
    public static I2<T1, T2> operator +(int a, I2<T1, T2> b) {
        b.index += a;
        return b;
    }

    public static I2<T1, T2> operator -(I2<T1, T2> a, int b) {
        a.index -= b;
        return a;
    }
    public static I2<T1, T2> operator -(int a, I2<T1, T2> b) {
        b.index = a - b.index;
        return b;
    }

    public static I2<T1, T2> operator *(I2<T1, T2> a, int b) {
        a.index *= b;
        return a;
    }
    public static I2<T1, T2> operator *(int a, I2<T1, T2> b) {
        b.index *= a;
        return b;
    }

    public static I2<T1, T2> operator /(I2<T1, T2> a, int b) {
        a.index /= b;
        return a;
    }
    public static I2<T1, T2> operator /(int a, I2<T1, T2> b) {
        b.index = a / b.index;
        return b;
    }

    public static I2<T1, T2> operator %(I2<T1, T2> a, int b) {
        a.index %= b;
        return a;
    }
    public static I2<T1, T2> operator %(int a, I2<T1, T2> b) {
        b.index = a % b.index;
        return b;
    }

    public static I2<T1, T2> operator &(I2<T1, T2> a, int b) {
        a.index &= b;
        return a;
    }
    public static I2<T1, T2> operator &(int a, I2<T1, T2> b) {
        b.index = a & b.index;
        return b;
    }

    public static I2<T1, T2> operator |(I2<T1, T2> a, int b) {
        a.index |= b;
        return a;
    }
    public static I2<T1, T2> operator |(int a, I2<T1, T2> b) {
        b.index = a | b.index;
        return b;
    }

    public static I2<T1, T2> operator ^(I2<T1, T2> a, int b) {
        a.index ^= b;
        return a;
    }
    public static I2<T1, T2> operator ^(int a, I2<T1, T2> b) {
        b.index = a ^ b.index;
        return b;
    }

    public static I2<T1, T2> operator <<(I2<T1, T2> a, int b) {
        a.index <<= b;
        return a;
    }

    public static I2<T1, T2> operator >>(I2<T1, T2> a, int b) {
        a.index >>= b;
        return a;
    }

    // Comparison operators

    public static bool operator ==(I2<T1, T2> a, int b) {
        return a.index == b;
    }
    public static bool operator ==(int a, I2<T1, T2> b) {
        return a == b.index;
    }

    public static bool operator !=(I2<T1, T2> a, int b) {
        return a.index != b;
    }
    public static bool operator !=(int a, I2<T1, T2> b) {
        return a != b.index;
    }

    public static bool operator <(I2<T1, T2> a, int b) {
        return a.index < b;
    }
    public static bool operator <(int a, I2<T1, T2> b) {
        return a < b.index;
    }

    public static bool operator >(I2<T1, T2> a, int b) {
        return a.index > b;
    }
    public static bool operator >(int a, I2<T1, T2> b) {
        return a > b.index;
    }

    public static bool operator <=(I2<T1, T2> a, int b) {
        return a.index <= b;
    }
    public static bool operator <=(int a, I2<T1, T2> b) {
        return a <= b.index;
    }

    public static bool operator >=(I2<T1, T2> a, int b) {
        return a.index >= b;
    }
    public static bool operator >=(int a, I2<T1, T2> b) {
        return a >= b.index;
    }
}

public struct I3<T1, T2, T3> {

    public int index;
    public T1 a;
    public T2 b;
    public T3 c;

    public I3(int index) {
        this.index = index;
        this.a = default(T1);
        this.b = default(T2);
        this.c = default(T3);
    }
    public I3(int index, T1 a) {
        this.index = index;
        this.a = a;
        this.b = default(T2);
        this.c = default(T3);
    }
    public I3(int index, T1 a, T2 b) {
        this.index = index;
        this.a = a;
        this.b = b;
        this.c = default(T3);
    }
    public I3(int index, T1 a, T2 b, T3 c) {
        this.index = index;
        this.a = a;
        this.b = b;
        this.c = c;
    }

    public override bool Equals(object obj) {
        if(!(obj is I3<T1, T2, T3>)) return false;
        I3<T1, T2, T3> other = (I3<T1, T2, T3>)obj;
        return index == other.index && EqualityComparer<T1>.Default.Equals(a, other.a) &&
            EqualityComparer<T2>.Default.Equals(b, other.b) &&
            EqualityComparer<T3>.Default.Equals(c, other.c);
    }

    public override int GetHashCode() {
        int hash = 17;
        hash = hash * 29 + index.GetHashCode();
        if(typeof(T1).IsValueType && !object.ReferenceEquals(a, null)) hash = hash * 29 + a.GetHashCode();
        if(typeof(T2).IsValueType && !object.ReferenceEquals(b, null)) hash = hash * 29 + b.GetHashCode();
        if(typeof(T3).IsValueType && !object.ReferenceEquals(c, null)) hash = hash * 29 + c.GetHashCode();
        return hash;
    }

    public override string ToString() {
        return index.ToString();
    }

    public static implicit operator I3<T1, T2, T3>(int other) {
        return new I3<T1, T2, T3>(other);
    }

    public static implicit operator int(I3<T1, T2, T3> other) {
        return other.index;
    }

    // Unary operators

    public static int operator +(I3<T1, T2, T3> a) {
        return +a.index;
    }

    public static int operator -(I3<T1, T2, T3> a) {
        return -a.index;
    }

    public static int operator ~(I3<T1, T2, T3> a) {
        return ~a.index;
    }

    public static I3<T1, T2, T3> operator ++(I3<T1, T2, T3> a) {
        a.index++;
        return a;
    }

    public static I3<T1, T2, T3> operator --(I3<T1, T2, T3> a) {
        a.index--;
        return a;
    }

    // Binary operators

    public static I3<T1, T2, T3> operator +(I3<T1, T2, T3> a, int b) {
        a.index += b;
        return a;
    }
    public static I3<T1, T2, T3> operator +(int a, I3<T1, T2, T3> b) {
        b.index += a;
        return b;
    }

    public static I3<T1, T2, T3> operator -(I3<T1, T2, T3> a, int b) {
        a.index -= b;
        return a;
    }
    public static I3<T1, T2, T3> operator -(int a, I3<T1, T2, T3> b) {
        b.index = a - b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator *(I3<T1, T2, T3> a, int b) {
        a.index *= b;
        return a;
    }
    public static I3<T1, T2, T3> operator *(int a, I3<T1, T2, T3> b) {
        b.index *= a;
        return b;
    }

    public static I3<T1, T2, T3> operator /(I3<T1, T2, T3> a, int b) {
        a.index /= b;
        return a;
    }
    public static I3<T1, T2, T3> operator /(int a, I3<T1, T2, T3> b) {
        b.index = a / b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator %(I3<T1, T2, T3> a, int b) {
        a.index %= b;
        return a;
    }
    public static I3<T1, T2, T3> operator %(int a, I3<T1, T2, T3> b) {
        b.index = a % b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator &(I3<T1, T2, T3> a, int b) {
        a.index &= b;
        return a;
    }
    public static I3<T1, T2, T3> operator &(int a, I3<T1, T2, T3> b) {
        b.index = a & b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator |(I3<T1, T2, T3> a, int b) {
        a.index |= b;
        return a;
    }
    public static I3<T1, T2, T3> operator |(int a, I3<T1, T2, T3> b) {
        b.index = a | b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator ^(I3<T1, T2, T3> a, int b) {
        a.index ^= b;
        return a;
    }
    public static I3<T1, T2, T3> operator ^(int a, I3<T1, T2, T3> b) {
        b.index = a ^ b.index;
        return b;
    }

    public static I3<T1, T2, T3> operator <<(I3<T1, T2, T3> a, int b) {
        a.index <<= b;
        return a;
    }

    public static I3<T1, T2, T3> operator >>(I3<T1, T2, T3> a, int b) {
        a.index >>= b;
        return a;
    }

    // Comparison operators

    public static bool operator ==(I3<T1, T2, T3> a, int b) {
        return a.index == b;
    }
    public static bool operator ==(int a, I3<T1, T2, T3> b) {
        return a == b.index;
    }

    public static bool operator !=(I3<T1, T2, T3> a, int b) {
        return a.index != b;
    }
    public static bool operator !=(int a, I3<T1, T2, T3> b) {
        return a != b.index;
    }

    public static bool operator <(I3<T1, T2, T3> a, int b) {
        return a.index < b;
    }
    public static bool operator <(int a, I3<T1, T2, T3> b) {
        return a < b.index;
    }

    public static bool operator >(I3<T1, T2, T3> a, int b) {
        return a.index > b;
    }
    public static bool operator >(int a, I3<T1, T2, T3> b) {
        return a > b.index;
    }

    public static bool operator <=(I3<T1, T2, T3> a, int b) {
        return a.index <= b;
    }
    public static bool operator <=(int a, I3<T1, T2, T3> b) {
        return a <= b.index;
    }

    public static bool operator >=(I3<T1, T2, T3> a, int b) {
        return a.index >= b;
    }
    public static bool operator >=(int a, I3<T1, T2, T3> b) {
        return a >= b.index;
    }
}