Sie sind auf Seite 1von 17

C# Basic C# for GH3d

Reference in a Nutshell

1st edition
author: Krzysztof Nazar
contact: krzysztof.a.nazar@gmail.com

parametric
support
C# Basic C# for GH3d
Reference in a Nutshell

1st edition
author: Krzysztof Nazar
contact: krzysztof.a.nazar@gmail.com

1. C# scripting component

C# scripting component can be found in Math tab in Script section. You can also place it on the canvas by
double-clicking and typing C#.
By default the component is created with x and y inputs and out and A outputs. out is a special output
printing text messages about errors, warning and script behaviour.
Names of the inputs and outputs (excluding out) become names of the variables inside a script. This
means you have to follow C# naming rules when you rename them. After zooming in pluses and minuses
appear which allow to add or remove inputs and outputs.

toggle script
editor shrinking

run script

variable names
Before you start scripting you have to specify input types by
right clicking on the component. In addition, you have three
ways of providing data (item, list, data tree).
Visit http://4.rhino3d.com/5/rhinocommon/ for full reference of
RhinoCommon types.
Note: outputs types are always set to generic object type.

the most generic base class

ways of providing data

RhinoCommon structures

RhinoCommon classes

2. Declaring variables
In C# you declare a variable by providing type followed by variables name. The rules are:
-it cant be any of the reserved keywords,
-it has to start with a letter or an underscore,
-it can contain letters, underscores and numbers,
-no whitespaces!
A declared variable is a container for a certain type of data. Assigning a value for the rst time is called
initialization.

data_type variable_name;
data_type variable_name = value;
data_type variable1, variable2, variable3;
data_type variable1 = value, variable2 = value;

3. Data types
C# has some built in data types. Numeric types are divided into:
1. Signed integrals (positive and negative values): sbyte, short, int, long
2. Unsigned integrals (only positive values): byte, ushort, uint, ulong
3. Real numbers: float, double, decimal
These types dier with an amount of memory occupied and the method of number representation.
There are also:
char type which represents a single Unicode character (eg. C)
string type which represents an immutable chain of char (eg C# course)
3b. Rhino and Grasshopper data types

In Rhino there are some custom data types designed to represent geometry. Some of the examples are
Point3d, Line, Poyline, Brep. Check out Rhino 5 SDK for full reference.
Every RhinoCommon type has its wrapper in Grasshopper. Wrapping RhinoCommon objects in GH objects
inside a script before outputting is a good practice which speeds up the program a lot.

Point3d GH_Point
Vector3d GH_Vector
Curve GH_Curve
Surface GH_Surface
double GH_Number
bool GH_Boolean

RhinoCommon and Grasshopper type comparison

4. Punctuators and operators


Puncuators help demarcate the structure of the program. Braces group multiple statements into a
statement block and semicolons signalize the end of a single statement (you dont need semicolons at the
ends of statement blocks).
{ } ;

An operator transforms and combines expressions. Following is the list of operators commonly used:

+ addition ++ incrementation += add and assign


- subtraction -- decrementation -= subtract and assign
* multiplication = assignment *= multiply and assign
/ division /= divide and assign
% remainder after division

5. Boolean type
C# bool type is a logical value that can be assigned either true or false. It is used to check conditions of
script or program execution.

bool myCondition;
declaration and initialization of a bool type variable myCondition
myCondition = true;

If you want to compare numbers you have to use one of the following operators. All of them return bool type
data. Notice the dierence between an assignment(single =) and comparison(double =) operators!

== equal < smaller than


!= not equal <= smaller or equal to
> larger than
&& conditional AND >= larger or equal to
|| conditional OR
! conditional NOT
6. Comments
Comments are lines of code ommited by compiler / interpreter. It is a good practice to explain complex
parts of the code. There are two types of comments: single and multiline.

//single line comment starts with double forward slashes


//here you can write whatever you want

/* multi line comment starts with a forward slash and an asterix.


You can write whatever you want but you have to end
the commentary block with an asterix and a forward slash: */

7. if() {...} else {...} instruction


You can control your program ow by using if() else instructions.

if (some_condition)
{
//doSomething; if() checks if the condition (bool type) is true or
} false and executes one of the statement blocks
else
{
//doSomethingElse;
}

bool itRains = false;


bool itIsSunny = true;
bool itIsWindy = false;
If it is not windy and it rains or it is
if (!itIsWindy && (itRains || itIsSunny )) sunny, take an umbrella.
{ Else, if it is windy and it rains, take a
TakeAnUmbrella(); coat.
} Else stay at home.
else if (itIsWindy && itRains)
{ In this case computer (or a robot) will
TakeACoat(); take an umbrella.
}
else
{
StayAtHome();
}

8. switch() { ... } instruction


If you have to check multiple conditions, it is convenient to use switch() instruction. It checks if provided
variable is equal to specied cases.

switch(some_variable)
{ if (some_variable == value1)
case value1: doSomething(); doSomething();
break; else if (some_variable == value2)
case value2: doSomethingElse(); doSomethingElse();
Is equivalent to: else if (some_variable == value3)
break;
case value3: doSomethingElse2(); doSomethingElse2();
break; else
default: doSomethingElse3(); doSomethingElse3();
break;
}
9. for() loop
Sometimes you want to do things in a loop. The most popular is a for() loop. This is the syntax:

for(initial_statement; condition; action_after_every_pass)


{
//instructions here
}

The most common approach is to use initial statement for declaring and initializing an indexing variable
(usually i, j, k, l, m, n) with 0, set the condition to indexing variable smaller than number of passes and
end every pass with incrementation of the indexing variable (eg. i++;)

for(int i=0; i<count; i++)


{ This loop would print values from zero to count-1
Print(i.ToString());
}

It is possible to prematurely end a loop with break; instruction or to jump to the next pass with continue;

for(int i=0; i<count; i++)


{
if (i % 2 != 0)
continue; //jump to next pass, i++
if (i == 10)
break; //exit loop

Print(i.ToString());
} This loop would print only even values from zero
to eight (at ten loop exits). If count is lower than 10,
then the loop will nish earlier.

for() loop is not starting if the condition is not met at the beginning.

10. while() loop


while(condition)
{
//do something
}

This is simpler version of a loop. It executes until the conditions are met and is not starting if they are not.
You can use break; and continue; like in a for loop.

int number=1;

while(number < 10)


{ This loop would print only uneven numbers from
//instructions here one to nine (at ten loop exits)
Print(number.ToString());
number += 2;
}
11. do {...} while(); loop
do {
//do something
}
while(condition);

The only dierence between while() and do ... while() loops is the latter executes at least once no matter if
the condition is met or not. Notice the semicolon after while().

int number = -10;


do {
Print(number.ToString());
number--; This loop would print -10
}
while(number < 0);

12. foreach() loop


foreach (type a_variable in a_list_or_array)
{
//do something with a_variable
}

foreach() loop is used to iterate through an array or a list.

int[] numbers = new int[]{1, 2, 4, 6, 7}; This loop would print:


1
foreach (int num in numbers) 2
{
4
Print(num.ToString());
} 6
7

13a. Arrays
type[] arrayName = new type[numberOfElements];

An array represents a xed number of variables (elements) of a particular type. They are always stored in a
contiguous block of memory what provides very ecient access. You need to declare how many elements
will an array have. You access a specic element in an array by providing its index in square brackets after
the arrays name. Indexing starts from 0!

int[] numbers = new int[50];


Two ways of initializing an array:
for(int i=0; i<numbers.Length; i++) 1) creating an array with 50 elements
{ of default value
numbers[i] = i; 2) creating an array and specifying all
} of the elements inside braces
int[] numbers2 = new int[]{1, 2, 4, 6, 7};
You can create a multidimensional array by declaring number of elements in more than one dimension. You
access a specic element by providing multiple indices separated by a comma.

int[,] numbers = new int[10, 20];

for(int i=0; i<numbers.GetLength(0); i++)


{ Creates an array with
for(int j=0; j<numbers.GetLength(1); i++) 10 rows and 20 colums
{
numbers[i, j] = i*j;
}
}

13b. Lists
List<type> listName = new List<type>();

Lists are similar to arrays but can have varying number of elements. You can add and remove data from a
list. And once again: indexing starts from 0!

List<int> numbers = new List<int>(); Two ways of initializing a list:


1) creating an empty list and lling it
for(int i=0; i<10; i++) with new elements
{ 2) creating a list and specifying the
numbers.Add(i); elements inside braces
}

List<int> numbers2 = new List<int>(){1, 2, 4, 6, 7};

for(int i=numbers.Count-1; i>0; i--)


{ Removing every even number from
if(numbers[i] % 2 == 0) the list. Notice iterating from the end
numbers.RemoveAt(i); towards the beginning.
}

14a. Methods

<modifiers> returning_type method_name(parameters)


{
// a set of instructions
}

A method performs some action in a series of instructions. It can receive some data from the caller by
parameters and output data back by specifying a return type. A method can specify void return type,
which indicates that its not returning any value to the caller. It is possible to output more than one variable
(list, array, data tree) via ref/out parameters.
public void PrintHello()
{
A non-returning method
Print(Hello);
}

public void PrintNumber(int num)


{
Print(num.ToString()); A non-returning method with single parameter
}

public int TwoTimes(int num);


{
return num*2; A method returning single integer number
}

//...
//inside a Runscript() method:
int number1 = 4;
int number2 = 0; Results:
Hello
PrintHello(); 4
PrintNumber(number1); 8
number2 = TwoTimes(number1);
PrintNumber(number2);
//...

Methods allow, inter alia, the following modiers:


Static modier static
Access modiers public internal private protected
Inheritance modiers new virtual abstract override sealed

14b. Method overloading


It is possible to have multiple methods with the same name but dierent implementation and parameter list.
Consider the following example:

public void PrintNumber(int intNum) Results:


{ 2
Print(intNum.ToString()); 3.14
}
number2: 3.14
public void PrintNumber(double realNum)
{
Print(realNum.ToString());
}
public void PrintNumber(double realNum,
Overloaded PrintNumber() method allows for
string label)
{ two types of parameters: int or double. This
Print(label + : + gives more elasticity.
realNum.ToString());
} It is not possible to overload methods by
changing only the return type.
//...
//inside a Runscript() method: Signatures of PrintNumber() methods:
int number1 = 2; PrintNumber(int)
double number2 = 3.14; PrintNumber(double)
string label = number2; PrintNumber(double, string)

PrintNumber(number1);
PrintNumber(number2);
PrintNumber(number2, label);
//...
14c. Out and ref modiers
It is possible to return more than one value to a caller. By default arguments are passed by value, what
means that a copy of object is created inside a method and destroyed after returning. You can however pass
by reference which means that an alias of passed object is created. This makes all changes persistent.

public void Iterate(int intNum)


{
intNum += 1;
Print(inside Iterate(): +
Results:
intNum.ToString());
} 2
public void IterateRef(ref int intNum) inside Iterate(): 3
{ 2
intNum += 1; inside IterateRef(): 3
Print(inside IterateRef(): + 3
intNum.ToString());
}

//...
//inside a Runscript() method:
int myNumber = 2;
Iterate() iterated only a local copy of myNumber.
Print(myNumber.ToString());

Iterate(myNumber); IterateRef() iterated the original myNumber


Print(myNumber.ToString()); variable through a reference

IterateRef(ref myNumber);
Print(myNumber.ToString());
//...

An out argument is like ref, except it need not be assigned before being passed into a method and has to
be assigned before it comes out of the method.
Notice you have to write ref or out both when you dene and call a method.

15a. Value and reference types.


There are four categories of data types in C#: values, references, generic type parameters and pointers. In this
section well focus on the rst two.
Value types comprise most built-in types (all numeric types, char and bool) and custom struct and enum
types.
Reference types comprise all class, array, delegate and interface types (including predened string type)..
The fundamental dierence is how these types are stored in computer memory.

public struct MyPoint2d_Struct


{
public double X;
public double Y; Value type
}

public class MyPoint2d_Class


{
public double X; Reference type
public double Y;
}
//...
//inside a Runscript() method: Declaration and
MyPoint2d_Struct point1 = new MyPoint2d_Struct(); initialization of
point1.X = 10; a custom-type object
point1.Y = 20;
Accessing a member
MyPoint2d_Struct point2 = point1; with . operator
point2.X = 15;

Print(point1.X.ToString()); // 10 Copying object


Print(point1.Y.ToString()); // 20
Print(point2.X.ToString()); // 15
Print(point2.Y.ToString()); // 20
//...

In case of value types, assignment operator creates an actual independent copy of an object. point1
wasnt aected by changes made on point2.

MyPoint2d_Struct instance:
point1: point2:

}
X 10 15
values
Y 20 20
memory

independent instances

//...
//inside a Runscript() method: Declaration and
MyPoint2d_Class point1 = new MyPoint2d_Class(); initialization of
point1.X = 10; a custom-type object
point1.Y = 20;
Accessing a member
MyPoint2d_Class point2 = point1; with . operator
point2.X = 15;
Copying a reference
Print(point1.X.ToString()); // 15
to an object!
Print(point1.Y.ToString()); // 20
Print(point2.X.ToString()); // 15
Print(point2.Y.ToString()); // 20
//...

In case of reference types, assignment operator creates a copy of reference to data. Reference is in fact a
variable storing a memory adress. Data referenced by point1 was aected by changes made on point2,
because they are pointing to the same place in memory!.

MyPoint2d_Class instance:
point1

}
15

}
Reference X
data data
Y 20
point2

references to the same adress in the memory


15b. Creating custom types: enum
This is the simplest custom type. Its name comes from enumerable. It lets you specify a group of named
numeric constants. Each enum member has an underlying integral value. These values are by default of
type int and are set to 0, 1, 2... in the declaration order.

public enum BoxSide


{
Front, //0
Back, //1
Left, //2 Results:
Right, //3 Were on top!
Top, //4 4
Bottom, //5
Invalid = -1 //explicitly defined -1
}

//...
//inside a Runscript() method:
BoxSide mySide = BoxSide.Top;
Note: you can cast enum to its underlying type.
if(mySide == BoxSide.Top)
Print(Were on top!);

Print(((int)mySide).ToString());
//...

15c. Creating custom types: class


Every program written in C# consists of classes. Classes are object descriptions. They can contain
information about what an object is and how it behaves.
Fields, properties and methods are called class members. The following is a classic example of a class.
Reminder: classes are reference types!

public class Point


{ This class contains two members (elds) which are public (can be
public double X; accessed from the outside of class) and of type double.
public double Y;
}

//... In order to create an object instance you have to declare a reference


//inside a Runscript() method: by typing: <object_type> name_of_your_choice;
Point myPoint = new Point();
myPoint.X = 10; After this you create an actual object in memory by typing:
myPoint.Y = 15.5; new <object_type>() and assign it to previously created reference
//...

You can access objects members by typing references name


followed by a dot ( . ) operator and a members name.

More complex classes may have the following:


before the class keyword: attributes and class modiers: public, internal,
abstract, sealed, static, unsafe and partial
following the class Name: generic type parameters, a base class and interfaces
Within the braces: class members: methods, properties, indexers, events, elds, constructors,
overloaded operators, nested types and a nalizer
A class usually contains a lot of dierent elds and methods. Lets add some functionality to the Point class.

public class Point


{
private double x;
private double y;

public void SetCoordinates(double newX, double newY)


{
x = newX;
y = newY;
} Note:
naming convention is that private
public double GetX(){return x;} elds start with a lowercase and
public double GetY(){return y;} public elds start with an
} uppercase
//...
//inside a Runscript() method:
Point myPoint = new Point();
myPoint.X = 10; //Error! X is private and can be accessed
//only from the inside of Point class
//...

Notice that now both x and y are private (can be accessed only from the inside of class). To make it
possible to set and read coordinates we dened three public methods. This approach is reasonable if you
want to prevent the user from inputting strange values (then a check inside the setting method is needed).

//...
//inside a Runscript() method:
Point myPoint = new Point(); Results:
myPoint.SetCoordinates(10, 15.3); 10
Print(myPoint.GetX().ToString()); 15.3
Print(myPoint.GetY().ToString());
//...

15d. Classes: properties


Although having Set... and Get... methods might be useful, it is unhandy for user to call them every time to
make changes or read values. It is also not intuitive. Look at this code:

//...
//inside a Runscript() method:
Point myPoint = new Point();
myPoint.SetX(10); Same member is accessed with dierent methods.
if(myPoint.GetX() > 20)
Print(Were so far!);
//...

There is a way to combine restricted only-method access with convenience of public elds. It is about using
properties.

public class Point


{
private double _x; //a private field...
private double _y; //a private field...
public double X { //a public property...
set {if(value>=0) _x = value; else _x = 0;}
get {return _x;}
}
public double Y { //a public property...
set {if(value>=0) _y = value; else _y = 0;}
get {return Y;}
}
}
Properties resemble methods but dont dene any arguments. The returning type is the type of an
underlying eld (which has to be explicitly dened by us, but can in case of auto-properties it is implicitly
dened by a compiler).
Inside the braces two behaviours are described: set, which accepts an implicit argument denoted with
keyword value and get which has to return some data to caller.
Now it is possible to use myPoint as follows:
//...
//inside a Runscript() method:
Point myPoint = new Point();
myPoint.X = 10;
myPoint.Y = -10; //set checks if provided value is smaller
//than 0 and assigns Y a default value of 0.

Print(myPoint.X.ToString()); //10
Print(myPoint.Y.ToString()); //0
//...

If you dont want to specify any special behaviour you can just restrict setting or getting a value by the user.
This type of a member is called an automatic property and the most common use is to make setting
private (controlled by the class) and getting being public.

public class Point


{
public double X { get; private set; } Underlying methods are implicitly
public double Y { get; private set; } dened by a compiler.
}

15e. Classes: constructors


public class class_name
{
public class_name(optional_arguments)
{
constructors instructions...
}
}

When an object is created in memory, a special method called a constructor is executed. It usually initializes
all elds and properties of an object and thus allows to set initial state.
Constructors are non-returning, public (in general) and have the same name as the class. If you dont dene
any constructor, then a default parameterless, empty constructor is implicitly created by the compiler.

public class Point


{
public Point(double initX, double initY){
X = initX;
Y = initY;
}

public double X { get; private set; } Note:


public double Y { get; private set; } constructors can be (and very
} often are) overloaded just as
regular methods
//...
//inside a Runscript() method:
Point myPoint = new Point(15, 16.2);

Print(myPoint.X.ToString()); //15
Print(myPoint.Y.ToString()); //16.2
//...
What if a constructor accepts arguments that are named exactly as the objects elds? No problem, just use
this keyword which denotes the object itself.

public class Point


{
public Point(double X, double Y){
this.X = X;
this.Y = Y;
}

public double X { get; private set; }


public double Y { get; private set; }
}

15f. Classes: inheritance

public class class_name : base_class


{
}

Inheritance is one of the most important aspects of every object-oriented programming (OOP) language. It
is used for code reuse and extensibility.

public class ColouredPoint : Point


{
public System.Drawing.Color ColourRGB { get; set; }
}

//...
//inside a Runscript() method:
ColouredPoint myColouredPoint = new myColouredPoint();
myColouredPoint.X = 3.14;
myColouredPoint.Y = 10.5;
myColouredPoint.ColourRGB = Color.FromArgb(0,0,0);
Print(myPoint.X.ToString()); //3.14
Print(myPoint.Y.ToString()); //10.5
Print(myPoint.ColourRGB .ToString()); //Color [A=255, R=0, G=0, B=0]
//...

ColouredPoint inherits all members of its base class Point. When a ColouredPoint object is created, a
default base class constructor is called rst. It is possible to reuse some base code like this:

public class ColouredPoint : Point


{
public ColouredPoint(double x, double y, int r, int g, int b) : base(x, y)
{
ColourRGB = Color.FromArgb(r, g, b);
}
Calling the base class
public System.Drawing.Color ColourRGB { get; set; } constructor with
} provided arguments
15g. Classes: overriding base methods
When a class is derived (inherits) from a class which has some virtual (or override) methods, it can
override their implementation.
As every object is implicitly derived from System.Object class which has a
public virtual string ToString()
method, we can override it in our Point class.

public class Point


{
public Point(double initX, double initY){
X = initX;
Y = initY;
}

public double X { get; private set; }


public double Y { get; private set; }
public override string ToString()
{
return string.Format(X: {0}, Y: {1}, X, Y);
}
}

//...
//inside a Runscript() method:
Point myPoint = new Point(15, 16.2);

Print(myPoint.ToString()); //X: 15, Y: 16.2


//...

15g. Static members


A static modier makes a member common to a whole class instead of being specic for a single
instance. Dierent from regular members, you access static members by typing class name followed by a
dot operator and static members name.

public class Point


{
public Point(double initX, double initY){ Note:
X = initX; A whole class can be marked as static.
Y = initY; If so, then it can contain only static
PointCount++; members. A very good example is
} System.Math class containing many
public static int PointCount = 0; useful methods and values.
public double X { get; private set; }
public double Y { get; private set; }
public override string ToString()
{
return string.Format(X: {0}, Y: {1}, X, Y);
}
}

//...
//inside a Runscript() method: Accessing a static member by
Point myPoint1 = new Point(15, 16.2); typing class name, a dot
Point myPoint2 = new Point(-3, 12); operator and a members name
Point myPoint3 = new Point(5, -7.8);

Print(Point.PointCount.ToString()); //3
//...
16. DataTree<> class
DataTree<> class is designed for handling branched data you know from Grasshopper. Consider the
following example:

(C)
(D)
(A)
(B) (E)

1. A list of two items is created 2. A list of two items is branched 3. A data tree is branched

In fact, the resulting data structure is a list of lists which are marked with branching info. We have the
following:
List<int> {2, 1}; with branching info {0;0;0;0} (made from the rst item(C) made from the rst item(A))
List<int> {2}; with branching info {0;0;0;1} (made from the second item(D) made from the rst item(A))
List<int> {2, 1}; with branching info {0;0;1;0} (made from the rst item(E) made from the second item(B))

This data tree would be created like this in C#:


DataTree<int> myTree = new DataTree<int>();

myTree.AddRange(new List<int>(){2, 1}, new GH_Path(0,0,0,0));


myTree.AddRange(new List<int>(){2}, new GH_Path(0,0,0,1));
myTree.AddRange(new List<int>(){2, 1}, new GH_Path(0,0,1,0));

Its that simple! When components try to combine data trees, they match lists by GH_Paths. Heres how you
iterate through a DataTree:

DataTree<int> myTree = new DataTree<int>();

myTree.AddRange(new List<int>(){2, 1}, new GH_Path(0,0,0,0));


myTree.AddRange(new List<int>(){2}, new GH_Path(0,0,0,1));
myTree.AddRange(new List<int>(){2, 1}, new GH_Path(0,0,1,0));

Property returning a
for(int i=0; i<myTree.BranchCount; i++)
{ number of branches
for(int j=0; j<myTree.Branch(i).Count; j++)
{ Method returning a
Print(myTree.Branch(i)[j].ToString()); branch as a list
}
} Accessing elements
of returned list

Das könnte Ihnen auch gefallen