달력

11

« 2024/11 »

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
2020. 1. 20. 00:19

The Sims 4™ Realm of Magic 영어2020. 1. 20. 00:19




The Sims 4™ Realm of Magic

심즈4 마법의 나라


https://www.youtube.com/watch?v=r_J7sxZo1aA&list=PLSnHrMX7SCvNtIERLfzYZ4xHXt3n5Pz8p&index=6








영어 자막


realm 영역, 범위

portal 입구, 정문, 문

swirl 빙빙 돌다, 소용돌이 치다. 소용돌이 치게 하다.

swirling 소용돌이치는

vortex 소용돌이

sage 현자

master 거장



Welcome to the Realm of Magic.

Finding the portal to this world

was just the beginning.

A swirling vortex threatens

to tear the Realm apart.

The only thing keeping it together…?

A balance between

the 3 houses of magic.

Each house has a Sage.

A master in their respective style.

I am Morgyn, Sage of the Untamed.

And I deem you worthy.

Cast down your old self and rise,

born anew...

A Spellcaster.

It takes practice, research,

and determination to become

a great Spellcaster.

Everything you learn is recorded here.

Specifically, the spells and potions

of the 4 schools of magic.

Specialize in one…

or bring balance to the realm

and master them all.

Practical magic simplifies

what was once complicated.

Why walk when you can teleport?

If it's power you crave,

harness the spells of the Untamed.

Or perhaps you fancy yourself a Deviant.

The School of Mischief offers

many ways to torment others.

As powerful as these spells are,

do not overlook the student of Alchemy.

When consumed,

potions can grant a Spellcaster

God-like abilities.

Or even counter the effects of Curses.

That reminds me.

Curses... They're a thing.

And you’ll want to avoid

them at all costs.

Oh my...

With experience,

you’ll progress through the ranks

and earn talent points.

These points can be exchanged

for Spellcaster perks.

Perks that give you magical advantages.

There are many ways

to reach the full potential

of your newfound power.

It's what you choose to do with it

that remains to be seen.





한글



마법 나라에 오신 것을 환영합니다

이 세계로 통하는 포털을 발견하는 것은

시작에 불과합니다

거센 소용돌이가 마법 나라를

산산조각 내려고 위협합니다

이곳이 무너지지 않게 유지하는 것은…?

세 마법의 집 사이의

균형입니다

각 마법의 집에는 현자가 있습니다

각자의 분야에서 거장이지요

나는 모르긴이라고 하는I길들지 않은 현자입니다

그리고 나는 당신이 귀한 인재라고 생각합니다

옛날의 자신을 버리고

새롭게 다시 태어나세요...

마법사로 말입니다

위대한 마법사가 되려면

연습, 연구, 결의가 필요합니다

당신이 배우는 모든 것은

마법서에 기록됩니다.

특히 4가지 마법 학파의

주문과 물약에 관한 것이요

한 가지 학파에 매진하거나....

마법 나라의 균형을 위해

모든 학파를 섭렵해도 됩니다

실용 마법은 예전에는 복잡했던 것을

단순하게 만들어줍니다

텔레포트를 할 수 있는데 왜 걸어갑니까?

당신이 갈망하는 것이 권력이라면

길들이지 않은 자의 마법을 사용해보세요

또는 사회 규범에 반하는 일탈자가 되고 싶을 수도 있겠지요

장난 학파는 남들을 괴롭히는

많은 방법을 알려줍니다

이 주문들이 강력하긴 하지만

연금술 학파를 가볍게 생각하지 마세요

물약은 마치 신과 같은

능력을 선사할 수 있습니다

하지만 저주 효과를 맞닥뜨릴 수도 있겠지요.

말이 나왔으니 말인데

저주는... 장난이 아닙니다

무슨 수를 써서라도

피하는 것이 좋을 겁니다

오.

경험이 쌓이면

등급이 올라갑니다

그리고 재능 포인트를 얻게 되지요

이 포인트를 사용해

마법사 특전을 구입할 수 있습니다

특전은 마법에 힘을 실어주지요

새로 발견한 힘의 완전한

잠재력에 도달하는

많은 방법이 있습니다

당신이 그 힘으로 무엇을 하는지가

앞으로 지켜봐야 할 일이겠지요

:
Posted by 지훈2
2017. 10. 8. 17:42

Programming guide - Generics 프로그래밍/C#2017. 10. 8. 17:42


  1. Generics
  2. Introduction to Generics
  3. Benefits of Generics
  4. Generic Type Parameters
  5. Constraints on Type Parameters
  6. Generic Classes
  7. Generic Interfaces
  8. Generic Methods
  9. Generic Delegates
  10. Differences Between C++ Templates and C# Generics
  11. Generics and Reflection
  12. Generics in the Run Time
  13. Generics in the .NET Framework Class Library



https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/generics/



Generics


Generics were added to version 2.0 of the C# language and the common language runtime (CLR). Generics introduce to the .NET Framework the concept of type parameters, which make it possible to design classes and methods that defer the specification of one or more types until the class or method is declared and instantiated by client code. For example, by using a generic type parameter T you can write a single class that other client code can use without incurring the cost or risk of runtime casts or boxing operations, as shown here:

C#
// Declare the generic class.
public class GenericList<T>
{
    void Add(T input) { }
}
class TestGenericList
{
    private class ExampleClass { }
    static void Main()
    {
        // Declare a list of type int.
        GenericList<int> list1 = new GenericList<int>();

        // Declare a list of type string.
        GenericList<string> list2 = new GenericList<string>();

        // Declare a list of type ExampleClass.
        GenericList<ExampleClass> list3 = new GenericList<ExampleClass>();
    }
}

Generics Overview

  • Use generic types to maximize code reuse, type safety, and performance.

  • The most common use of generics is to create collection classes.

  • The .NET Framework class library contains several new generic collection classes in the System.Collections.Generic namespace. These should be used whenever possible instead of classes such as ArrayList in the System.Collections namespace.

  • You can create your own generic interfaces, classes, methods, events and delegates.

  • Generic classes may be constrained to enable access to methods on particular data types.

  • Information on the types that are used in a generic data type may be obtained at run-time by using reflection.

For more information:





Introduction to Generics


Generic classes and methods combine reusability, type safety and efficiency in a way that their non-generic counterparts cannot. Generics are most frequently used with collections and the methods that operate on them. Version 2.0 of the .NET Framework class library provides a new namespace, System.Collections.Generic, which contains several new generic-based collection classes. It is recommended that all applications that target the .NET Framework 2.0 and later use the new generic collection classes instead of the older non-generic counterparts such as ArrayList. For more information, see Generics in the .NET Framework Class Library.

Of course, you can also create custom generic types and methods to provide your own generalized solutions and design patterns that are type-safe and efficient. The following code example shows a simple generic linked-list class for demonstration purposes. (In most cases, you should use the List<T> class provided by the .NET Framework class library instead of creating your own.) The type parameter T is used in several locations where a concrete type would ordinarily be used to indicate the type of the item stored in the list. It is used in the following ways:

  • As the type of a method parameter in the AddHead method.

  • As the return type of the public method GetNext and the Data property in the nested Node class.

  • As the type of the private member data in the nested class.

Note that T is available to the nested Node class. When GenericList<T> is instantiated with a concrete type, for example as a GenericList<int>, each occurrence of T will be replaced with int.1

C#
// type parameter T in angle brackets
public class GenericList<T> 
{
    // The nested class is also generic on T.
    private class Node
    {
        // T used in non-generic constructor.
        public Node(T t)
        {
            next = null;
            data = t;
        }

        private Node next;
        public Node Next
        {
            get { return next; }
            set { next = value; }
        }
        
        // T as private member data type.
        private T data;

        // T as return type of property.
        public T Data  
        {
            get { return data; }
            set { data = value; }
        }
    }

    private Node head;
    
    // constructor
    public GenericList() 
    {
        head = null;
    }

    // T as method parameter type:
    public void AddHead(T t) 
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }
}

The following code example shows how client code uses the generic GenericList<T> class to create a list of integers. Simply by changing the type argument, the following code could easily be modified to create lists of strings or any other custom type:

C#
class TestGenericList
{
    static void Main()
    {
        // int is the type argument
        GenericList<int> list = new GenericList<int>();

        for (int x = 0; x < 10; x++)
        {
            list.AddHead(x);
        }

        foreach (int i in list)
        {
            System.Console.Write(i + " ");
        }
        System.Console.WriteLine("\nDone");
    }
}






Benefits of Generics


Generics provide the solution to a limitation in earlier versions of the common language runtime and the C# language in which generalization is accomplished by casting types to and from the universal base type Object. By creating a generic class, you can create a collection that is type-safe at compile-time.

The limitations of using non-generic collection classes can be demonstrated by writing a short program that uses the ArrayList collection class from the .NET Framework class library. ArrayList is a highly convenient collection class that can be used without modification to store any reference or value type.

C#
// The .NET Framework 1.1 way to create a list:
System.Collections.ArrayList list1 = new System.Collections.ArrayList();
list1.Add(3);
list1.Add(105);

System.Collections.ArrayList list2 = new System.Collections.ArrayList();
list2.Add("It is raining in Redmond.");
list2.Add("It is snowing in the mountains.");

But this convenience comes at a cost. Any reference or value type that is added to an ArrayList is implicitly upcast to Object. If the items are value types, they must be boxed when they are added to the list, and unboxed when they are retrieved. Both the casting and the boxing and unboxing operations decrease performance; the effect of boxing and unboxing can be very significant in scenarios where you must iterate over large collections.

The other limitation is lack of compile-time type checking; because an ArrayList casts everything to Object, there is no way at compile-time to prevent client code from doing something such as this:

C#
System.Collections.ArrayList list = new System.Collections.ArrayList();
// Add an integer to the list.
list.Add(3);
// Add a string to the list. This will compile, but may cause an error later.
list.Add("It is raining in Redmond.");

int t = 0;
// This causes an InvalidCastException to be returned.
foreach (int x in list)
{
    t += x;
}

Although perfectly acceptable and sometimes intentional if you are creating a heterogeneous collection, combining strings and ints in a single ArrayList is more likely to be a programming error, and this error will not be detected until runtime.

In versions 1.0 and 1.1 of the C# language, you could avoid the dangers of generalized code in the .NET Framework base class library collection classes only by writing your own type specific collections. Of course, because such a class is not reusable for more than one data type, you lose the benefits of generalization, and you have to rewrite the class for each type that will be stored.

What ArrayList and other similar classes really need is a way for client code to specify, on a per-instance basis, the particular data type that they intend to use. That would eliminate the need for the upcast to T:System.Object and would also make it possible for the compiler to do type checking. In other words, ArrayList needs a type parameter. That is exactly what generics provide. In the generic List<T> collection, in the N:System.Collections.Generic namespace, the same operation of adding items to the collection resembles this:

C#
// The .NET Framework 2.0 way to create a list
List<int> list1 = new List<int>();

// No boxing, no casting:
list1.Add(3);

// Compile-time error:
// list1.Add("It is raining in Redmond.");

For client code, the only added syntax with List<T> compared to ArrayList is the type argument in the declaration and instantiation. In return for this slightly more coding complexity, you can create a list that is not only safer than ArrayList, but also significantly faster, especially when the list items are value types.






Generic Type Parameters


In a generic type or method definition, a type parameters is a placeholder for a specific type that a client specifies when they instantiate a variable of the generic type. A generic class, such as GenericList<T> listed in Introduction to Generics, cannot be used as-is because it is not really a type; it is more like a blueprint for a type. To use GenericList<T>, client code must declare and instantiate a constructed type by specifying a type argument inside the angle brackets. The type argument for this particular class can be any type recognized by the compiler. Any number of constructed type instances can be created, each one using a different type argument, as follows:

C#
GenericList<float> list1 = new GenericList<float>();
GenericList<ExampleClass> list2 = new GenericList<ExampleClass>();
GenericList<ExampleStruct> list3 = new GenericList<ExampleStruct>();

In each of these instances of GenericList<T>, every occurrence of T in the class will be substituted at run time with the type argument. By means of this substitution, we have created three separate type-safe and efficient objects using a single class definition. For more information on how this substitution is performed by the CLR, see Generics in the Run Time.

Type Parameter Naming Guidelines

  • Do name generic type parameters with descriptive names, unless a single letter name is completely self explanatory and a descriptive name would not add value.

    C#
    public interface ISessionChannel<TSession> { /*...*/ }
    public delegate TOutput Converter<TInput, TOutput>(TInput from);
    public class List<T> { /*...*/ }
    
  • Consider using T as the type parameter name for types with one single letter type parameter.

    C#
    public int IComparer<T>() { return 0; }
    public delegate bool Predicate<T>(T item);
    public struct Nullable<T> where T : struct { /*...*/ }
    
  • Do prefix descriptive type parameter names with "T".

    C#
    public interface ISessionChannel<TSession>
    {
        TSession Session { get; }
    }
    
  • Consider indicating constraints placed on a type parameter in the name of parameter. For example, a parameter constrained to ISession may be called TSession.






Constraints on Type Parameters


When you define a generic class, you can apply restrictions to the kinds of types that client code can use for type arguments when it instantiates your class. If client code tries to instantiate your class by using a type that is not allowed by a constraint, the result is a compile-time error. These restrictions are called constraints. Constraints are specified by using the where contextual keyword. The following table lists the six types of constraints:

ConstraintDescription
where T: structThe type argument must be a value type. Any value type except Nullable can be specified. See Using Nullable Types for more information.
where T : classThe type argument must be a reference type; this applies also to any class, interface, delegate, or array type.
where T : new()The type argument must have a public parameterless constructor. When used together with other constraints, the new() constraint must be specified last.
where T : <base class name>The type argument must be or derive from the specified base class.
where T : <interface name>The type argument must be or implement the specified interface. Multiple interface constraints can be specified. The constraining interface can also be generic.
where T : UThe type argument supplied for T must be or derive from the argument supplied for U.

Why Use Constraints

If you want to examine an item in a generic list to determine whether it is valid or to compare it to some other item, the compiler must have some guarantee that the operator or method it has to call will be supported by any type argument that might be specified by client code. This guarantee is obtained by applying one or more constraints to your generic class definition. For example, the base class constraint tells the compiler that only objects of this type or derived from this type will be used as type arguments. Once the compiler has this guarantee, it can allow methods of that type to be called in the generic class. Constraints are applied by using the contextual keyword where. The following code example demonstrates the functionality we can add to the GenericList<T> class (in Introduction to Generics) by applying a base class constraint.

C#
public class Employee
{
    private string name;
    private int id;

    public Employee(string s, int i)
    {
        name = s;
        id = i;
    }

    public string Name
    {
        get { return name; }
        set { name = value; }
    }

    public int ID
    {
        get { return id; }
        set { id = value; }
    }
}

public class GenericList<T> where T : Employee
{
    private class Node
    {
        private Node next;
        private T data;

        public Node(T t)
        {
            next = null;
            data = t;
        }

        public Node Next
        {
            get { return next; }
            set { next = value; }
        }

        public T Data
        {
            get { return data; }
            set { data = value; }
        }
    }

    private Node head;

    public GenericList() //constructor
    {
        head = null;
    }

    public void AddHead(T t)
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    public IEnumerator<T> GetEnumerator()
    {
        Node current = head;

        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    public T FindFirstOccurrence(string s)
    {
        Node current = head;
        T t = null;

        while (current != null)
        {
            //The constraint enables access to the Name property.
            if (current.Data.Name == s)
            {
                t = current.Data;
                break;
            }
            else
            {
                current = current.Next;
            }
        }
        return t;
    }
}

The constraint enables the generic class to use the Employee.Name property because all items of type T are guaranteed to be either an Employee object or an object that inherits from Employee.

Multiple constraints can be applied to the same type parameter, and the constraints themselves can be generic types, as follows:

C#
class EmployeeList<T> where T : Employee, IEmployee, System.IComparable<T>, new()
{
    // ...
}

By constraining the type parameter, you increase the number of allowable operations and method calls to those supported by the constraining type and all types in its inheritance hierarchy. Therefore, when you design generic classes or methods, if you will be performing any operation on the generic members beyond simple assignment or calling any methods not supported by System.Object, you will have to apply constraints to the type parameter.

When applying the where T : class constraint, avoid the == and != operators on the type parameter because these operators will test for reference identity only, not for value equality. This is the case even if these operators are overloaded in a type that is used as an argument. The following code illustrates this point; the output is false even though the String class overloads the == operator.

C#
public static void OpTest<T>(T s, T t) where T : class
{
    System.Console.WriteLine(s == t);
}
static void Main()
{
    string s1 = "target";
    System.Text.StringBuilder sb = new System.Text.StringBuilder("target");
    string s2 = sb.ToString();
    OpTest<string>(s1, s2);
}

The reason for this behavior is that, at compile time, the compiler only knows that T is a reference type, and therefore must use the default operators that are valid for all reference types. If you must test for value equality, the recommended way is to also apply the where T : IComparable<T> constraint and implement that interface in any class that will be used to construct the generic class.

Constraining Multiple Parameters

You can apply constraints to multiple parameters, and multiple constraints to a single parameter, as shown in the following example:

C#
class Base { }
class Test<T, U>
    where U : struct
    where T : Base, new() { }

Unbounded Type Parameters

Type parameters that have no constraints, such as T in public class SampleClass<T>{}, are called unbounded type parameters. Unbounded type parameters have the following rules:

  • The != and == operators cannot be used because there is no guarantee that the concrete type argument will support these operators.

  • They can be converted to and from System.Object or explicitly converted to any interface type.

  • You can compare to null. If an unbounded parameter is compared to null, the comparison will always return false if the type argument is a value type.

Type Parameters as Constraints

The use of a generic type parameter as a constraint is useful when a member function with its own type parameter has to constrain that parameter to the type parameter of the containing type, as shown in the following example:

C#
class List<T>
{
    void Add<U>(List<U> items) where U : T {/*...*/}
}

In the previous example, T is a type constraint in the context of the Add method, and an unbounded type parameter in the context of the List class.

Type parameters can also be used as constraints in generic class definitions. Note that the type parameter must be declared within the angle brackets together with any other type parameters:

C#
//Type parameter V is used as a type constraint.
public class SampleClass<T, U, V> where T : V { }

The usefulness of type parameters as constraints with generic classes is very limited because the compiler can assume nothing about the type parameter except that it derives from System.Object. Use type parameters as constraints on generic classes in scenarios in which you want to enforce an inheritance relationship between two type parameters.






Generic Classes


Generic classes encapsulate operations that are not specific to a particular data type. The most common use for generic classes is with collections like linked lists, hash tables, stacks, queues, trees, and so on. Operations such as adding and removing items from the collection are performed in basically the same way regardless of the type of data being stored.

For most scenarios that require collection classes, the recommended approach is to use the ones provided in the .NET Framework class library. For more information about using these classes, see Generics in the .NET Framework Class Library.

Typically, you create generic classes by starting with an existing concrete class, and changing types into type parameters one at a time until you reach the optimal balance of generalization and usability. When creating your own generic classes, important considerations include the following:

  • Which types to generalize into type parameters.

    As a rule, the more types you can parameterize, the more flexible and reusable your code becomes. However, too much generalization can create code that is difficult for other developers to read or understand.

  • What constraints, if any, to apply to the type parameters (See Constraints on Type Parameters).

    A good rule is to apply the maximum constraints possible that will still let you handle the types you must handle. For example, if you know that your generic class is intended for use only with reference types, apply the class constraint. That will prevent unintended use of your class with value types, and will enable you to use the as operator on T, and check for null values.

  • Whether to factor generic behavior into base classes and subclasses.

    Because generic classes can serve as base classes, the same design considerations apply here as with non-generic classes. See the rules about inheriting from generic base classes later in this topic.

  • Whether to implement one or more generic interfaces.

    For example, if you are designing a class that will be used to create items in a generics-based collection, you may have to implement an interface such as IComparable<T> where T is the type of your class.

For an example of a simple generic class, see Introduction to Generics.

The rules for type parameters and constraints have several implications for generic class behavior, especially regarding inheritance and member accessibility. Before proceeding, you should understand some terms. For a generic class Node<T>, client code can reference the class either by specifying a type argument, to create a closed constructed type (Node<int>). Alternatively, it can leave the type parameter unspecified, for example when you specify a generic base class, to create an open constructed type (Node<T>). Generic classes can inherit from concrete, closed constructed, or open constructed base classes:

C#
class BaseNode { }
class BaseNodeGeneric<T> { }

// concrete type
class NodeConcrete<T> : BaseNode { }

//closed constructed type
class NodeClosed<T> : BaseNodeGeneric<int> { }

//open constructed type 
class NodeOpen<T> : BaseNodeGeneric<T> { }

Non-generic, in other words, concrete, classes can inherit from closed constructed base classes, but not from open constructed classes or from type parameters because there is no way at run time for client code to supply the type argument required to instantiate the base class.

C#
//No error
class Node1 : BaseNodeGeneric<int> { }

//Generates an error
//class Node2 : BaseNodeGeneric<T> {}

//Generates an error
//class Node3 : T {}

Generic classes that inherit from open constructed types must supply type arguments for any base class type parameters that are not shared by the inheriting class, as demonstrated in the following code:

C#
class BaseNodeMultiple<T, U> { }

//No error
class Node4<T> : BaseNodeMultiple<T, int> { }

//No error
class Node5<T, U> : BaseNodeMultiple<T, U> { }

//Generates an error
//class Node6<T> : BaseNodeMultiple<T, U> {} 

Generic classes that inherit from open constructed types must specify constraints that are a superset of, or imply, the constraints on the base type:

C#
class NodeItem<T> where T : System.IComparable<T>, new() { }
class SpecialNodeItem<T> : NodeItem<T> where T : System.IComparable<T>, new() { }

Generic types can use multiple type parameters and constraints, as follows:

C#
class SuperKeyType<K, V, U>
    where U : System.IComparable<U>
    where V : new()
{ }

Open constructed and closed constructed types can be used as method parameters:

C#
void Swap<T>(List<T> list1, List<T> list2)
{
    //code to swap items
}

void Swap(List<int> list1, List<int> list2)
{
    //code to swap items
}

If a generic class implements an interface, all instances of that class can be cast to that interface.

Generic classes are invariant. In other words, if an input parameter specifies a List<BaseClass>, you will get a compile-time error if you try to provide a List<DerivedClass>.






Generic Interfaces


It is often useful to define interfaces either for generic collection classes, or for the generic classes that represent items in the collection. The preference for generic classes is to use generic interfaces, such as IComparable<T>rather than IComparable, in order to avoid boxing and unboxing operations on value types. The .NET Framework class library defines several generic interfaces for use with the collection classes in the System.Collections.Genericnamespace.

When an interface is specified as a constraint on a type parameter, only types that implement the interface can be used. The following code example shows a SortedList<T> class that derives from the GenericList<T> class. For more information, see Introduction to GenericsSortedList<T> adds the constraint where T : IComparable<T>. This enables the BubbleSort method in SortedList<T> to use the generic CompareTo method on list elements. In this example, list elements are a simple class, Person, that implements IComparable<Person>.

C#
//Type parameter T in angle brackets.
public class GenericList<T> : System.Collections.Generic.IEnumerable<T>
{
    protected Node head;
    protected Node current = null;

    // Nested class is also generic on T
    protected class Node
    {
        public Node next;
        private T data;  //T as private member datatype

        public Node(T t)  //T used in non-generic constructor
        {
            next = null;
            data = t;
        }

        public Node Next
        {
            get { return next; }
            set { next = value; }
        }

        public T Data  //T as return type of property
        {
            get { return data; }
            set { data = value; }
        }
    }

    public GenericList()  //constructor
    {
        head = null;
    }

    public void AddHead(T t)  //T as method parameter type
    {
        Node n = new Node(t);
        n.Next = head;
        head = n;
    }

    // Implementation of the iterator
    public System.Collections.Generic.IEnumerator<T> GetEnumerator()
    {
        Node current = head;
        while (current != null)
        {
            yield return current.Data;
            current = current.Next;
        }
    }

    // IEnumerable<T> inherits from IEnumerable, therefore this class 
    // must implement both the generic and non-generic versions of 
    // GetEnumerator. In most cases, the non-generic method can 
    // simply call the generic method.
    System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
    {
        return GetEnumerator();
    }
}

public class SortedList<T> : GenericList<T> where T : System.IComparable<T>
{
    // A simple, unoptimized sort algorithm that 
    // orders list elements from lowest to highest:

    public void BubbleSort()
    {
        if (null == head || null == head.Next)
        {
            return;
        }
        bool swapped;

        do
        {
            Node previous = null;
            Node current = head;
            swapped = false;

            while (current.next != null)
            {
                //  Because we need to call this method, the SortedList
                //  class is constrained on IEnumerable<T>
                if (current.Data.CompareTo(current.next.Data) > 0)
                {
                    Node tmp = current.next;
                    current.next = current.next.next;
                    tmp.next = current;

                    if (previous == null)
                    {
                        head = tmp;
                    }
                    else
                    {
                        previous.next = tmp;
                    }
                    previous = tmp;
                    swapped = true;
                }
                else
                {
                    previous = current;
                    current = current.next;
                }
            }
        } while (swapped);
    }
}

// A simple class that implements IComparable<T> using itself as the 
// type argument. This is a common design pattern in objects that 
// are stored in generic lists.
public class Person : System.IComparable<Person>
{
    string name;
    int age;

    public Person(string s, int i)
    {
        name = s;
        age = i;
    }

    // This will cause list elements to be sorted on age values.
    public int CompareTo(Person p)
    {
        return age - p.age;
    }

    public override string ToString()
    {
        return name + ":" + age;
    }

    // Must implement Equals.
    public bool Equals(Person p)
    {
        return (this.age == p.age);
    }
}

class Program
{
    static void Main()
    {
        //Declare and instantiate a new generic SortedList class.
        //Person is the type argument.
        SortedList<Person> list = new SortedList<Person>();

        //Create name and age values to initialize Person objects.
        string[] names = new string[] 
        { 
            "Franscoise", 
            "Bill", 
            "Li", 
            "Sandra", 
            "Gunnar", 
            "Alok", 
            "Hiroyuki", 
            "Maria", 
            "Alessandro", 
            "Raul" 
        };

        int[] ages = new int[] { 45, 19, 28, 23, 18, 9, 108, 72, 30, 35 };

        //Populate the list.
        for (int x = 0; x < 10; x++)
        {
            list.AddHead(new Person(names[x], ages[x]));
        }

        //Print out unsorted list.
        foreach (Person p in list)
        {
            System.Console.WriteLine(p.ToString());
        }
        System.Console.WriteLine("Done with unsorted list");

        //Sort the list.
        list.BubbleSort();

        //Print out sorted list.
        foreach (Person p in list)
        {
            System.Console.WriteLine(p.ToString());
        }
        System.Console.WriteLine("Done with sorted list");
    }
}

Multiple interfaces can be specified as constraints on a single type, as follows:

C#
class Stack<T> where T : System.IComparable<T>, IEnumerable<T>
{
}

An interface can define more than one type parameter, as follows:

C#
interface IDictionary<K, V>
{
}

The rules of inheritance that apply to classes also apply to interfaces:

C#
interface IMonth<T> { }

interface IJanuary     : IMonth<int> { }  //No error
interface IFebruary<T> : IMonth<int> { }  //No error
interface IMarch<T>    : IMonth<T> { }    //No error
//interface IApril<T>  : IMonth<T, U> {}  //Error

Generic interfaces can inherit from non-generic interfaces if the generic interface is contra-variant, which means it only uses its type parameter as a return value. In the .NET Framework class library, IEnumerable<T> inherits from IEnumerable because IEnumerable<T> only uses T in the return value of GetEnumerator and in the Currentproperty getter.

Concrete classes can implement closed constructed interfaces, as follows:

C#
interface IBaseInterface<T> { }

class SampleClass : IBaseInterface<string> { }

Generic classes can implement generic interfaces or closed constructed interfaces as long as the class parameter list supplies all arguments required by the interface, as follows:

C#
interface IBaseInterface1<T> { }
interface IBaseInterface2<T, U> { }

class SampleClass1<T> : IBaseInterface1<T> { }          //No error
class SampleClass2<T> : IBaseInterface2<T, string> { }  //No error

The rules that control method overloading are the same for methods within generic classes, generic structs, or generic interfaces. For more information, see Generic Methods.






Generic Methods


A generic method is a method that is declared with type parameters, as follows:

C#
static void Swap<T>(ref T lhs, ref T rhs)
{
    T temp;
    temp = lhs;
    lhs = rhs;
    rhs = temp;
}

The following code example shows one way to call the method by using int for the type argument:

C#
public static void TestSwap()
{
    int a = 1;
    int b = 2;

    Swap<int>(ref a, ref b);
    System.Console.WriteLine(a + " " + b);
}

You can also omit the type argument and the compiler will infer it. The following call to Swap is equivalent to the previous call:

C#
Swap(ref a, ref b);

The same rules for type inference apply to static methods and instance methods. The compiler can infer the type parameters based on the method arguments you pass in; it cannot infer the type parameters only from a constraint or return value. Therefore type inference does not work with methods that have no parameters. Type inference occurs at compile time before the compiler tries to resolve overloaded method signatures. The compiler applies type inference logic to all generic methods that share the same name. In the overload resolution step, the compiler includes only those generic methods on which type inference succeeded.

Within a generic class, non-generic methods can access the class-level type parameters, as follows:

C#
class SampleClass<T>
{
    void Swap(ref T lhs, ref T rhs) { }
}

If you define a generic method that takes the same type parameters as the containing class, the compiler generates warning CS0693 because within the method scope, the argument supplied for the inner T hides the argument supplied for the outer T. If you require the flexibility of calling a generic class method with type arguments other than the ones provided when the class was instantiated, consider providing another identifier for the type parameter of the method, as shown in GenericList2<T> in the following example.

C#
class GenericList<T>
{
    // CS0693
    void SampleMethod<T>() { }
}

class GenericList2<T>
{
    //No warning
    void SampleMethod<U>() { }
}

Use constraints to enable more specialized operations on type parameters in methods. This version of Swap<T>, now named SwapIfGreater<T>, can only be used with type arguments that implement IComparable<T>.

C#
void SwapIfGreater<T>(ref T lhs, ref T rhs) where T : System.IComparable<T>
{
    T temp;
    if (lhs.CompareTo(rhs) > 0)
    {
        temp = lhs;
        lhs = rhs;
        rhs = temp;
    }
}

Generic methods can be overloaded on several type parameters. For example, the following methods can all be located in the same class:+

C#
void DoWork() { }
void DoWork<T>() { }
void DoWork<T, U>() { }






Generics and Arrays


In C# 2.0 and later, single-dimensional arrays that have a lower bound of zero automatically implement IList<T>. This enables you to create generic methods that can use the same code to iterate through arrays and other collection types. This technique is primarily useful for reading data in collections. The IList<T> interface cannot be used to add or remove elements from an array. An exception will be thrown if you try to call an IList<T> method such as RemoveAt on an array in this context.

The following code example demonstrates how a single generic method that takes an IList<T> input parameter can iterate through both a list and an array, in this case an array of integers.

C#
class Program
{
    static void Main()
    {
        int[] arr = { 0, 1, 2, 3, 4 };
        List<int> list = new List<int>();

        for (int x = 5; x < 10; x++)
        {
            list.Add(x);
        }

        ProcessItems<int>(arr);
        ProcessItems<int>(list);
    }

    static void ProcessItems<T>(IList<T> coll)
    {
        // IsReadOnly returns True for the array and False for the List.
        System.Console.WriteLine
            ("IsReadOnly returns {0} for this collection.",
            coll.IsReadOnly);

        // The following statement causes a run-time exception for the 
        // array, but not for the List.
        //coll.RemoveAt(4);

        foreach (T item in coll)
        {
            System.Console.Write(item.ToString() + " ");
        }
        System.Console.WriteLine();
    }
}






Generic Delegates


A delegate can define its own type parameters. Code that references the generic delegate can specify the type argument to create a closed constructed type, just like when instantiating a generic class or calling a generic method, as shown in the following example:

C#
public delegate void Del<T>(T item);
public static void Notify(int i) { }

Del<int> m1 = new Del<int>(Notify);

C# version 2.0 has a new feature called method group conversion, which applies to concrete as well as generic delegate types, and enables you to write the previous line with this simplified syntax:

C#
Del<int> m2 = Notify;

Delegates defined within a generic class can use the generic class type parameters in the same way that class methods do.

C#
class Stack<T>
{
    T[] items;
    int index;

    public delegate void StackDelegate(T[] items);
}

Code that references the delegate must specify the type argument of the containing class, as follows:

C#
private static void DoWork(float[] items) { }

public static void TestStack()
{
    Stack<float> s = new Stack<float>();
    Stack<float>.StackDelegate d = DoWork;
}

Generic delegates are especially useful in defining events based on the typical design pattern because the sender argument can be strongly typed and no longer has to be cast to and from Object.

C#
delegate void StackEventHandler<T, U>(T sender, U eventArgs);

class Stack<T>
{
    public class StackEventArgs : System.EventArgs { }
    public event StackEventHandler<Stack<T>, StackEventArgs> stackEvent;

    protected virtual void OnStackChanged(StackEventArgs a)
    {
        stackEvent(this, a);
    }
}

class SampleClass
{
    public void HandleStackChange<T>(Stack<T> stack, Stack<T>.StackEventArgs args) { }
}

public static void Test()
{
    Stack<double> s = new Stack<double>();
    SampleClass o = new SampleClass();
    s.stackEvent += o.HandleStackChange;
}






Differences Between C++ Templates and C# Generics


C# Generics and C++ templates are both language features that provide support for parameterized types. However, there are many differences between the two. At the syntax level, C# generics are a simpler approach to parameterized types without the complexity of C++ templates. In addition, C# does not attempt to provide all of the functionality that C++ templates provide. At the implementation level, the primary difference is that C# generic type substitutions are performed at runtime and generic type information is thereby preserved for instantiated objects. For more information, see Generics in the Run Time.

The following are the key differences between C# Generics and C++ templates:

  • C# generics do not provide the same amount of flexibility as C++ templates. For example, it is not possible to call arithmetic operators in a C# generic class, although it is possible to call user defined operators.

  • C# does not allow non-type template parameters, such as template C<int i> {}.

  • C# does not support explicit specialization; that is, a custom implementation of a template for a specific type.

  • C# does not support partial specialization: a custom implementation for a subset of the type arguments.

  • C# does not allow the type parameter to be used as the base class for the generic type.

  • C# does not allow type parameters to have default types.

  • In C#, a generic type parameter cannot itself be a generic, although constructed types can be used as generics. C++ does allow template parameters.

  • C++ allows code that might not be valid for all type parameters in the template, which is then checked for the specific type used as the type parameter. C# requires code in a class to be written in such a way that it will work with any type that satisfies the constraints. For example, in C++ it is possible to write a function that uses the arithmetic operators + and - on objects of the type parameter, which will produce an error at the time of instantiation of the template with a type that does not support these operators. C# disallows this; the only language constructs allowed are those that can be deduced from the constraints.






Generics in the Run Time


When a generic type or method is compiled into Microsoft intermediate language (MSIL), it contains metadata that identifies it as having type parameters. How the MSIL for a generic type is used differs based on whether the supplied type parameter is a value type or reference type.

When a generic type is first constructed with a value type as a parameter, the runtime creates a specialized generic type with the supplied parameter or parameters substituted in the appropriate locations in the MSIL. Specialized generic types are created one time for each unique value type that is used as a parameter.

For example, suppose your program code declared a stack that is constructed of integers:

C#
Stack<int> stack;

At this point, the runtime generates a specialized version of the Stack<T> class that has the integer substituted appropriately for its parameter. Now, whenever your program code uses a stack of integers, the runtime reuses the generated specialized Stack<T> class. In the following example, two instances of a stack of integers are created, and they share a single instance of the Stack<int> code:

C#
Stack<int> stackOne = new Stack<int>();
Stack<int> stackTwo = new Stack<int>();

However, suppose that another Stack<T> class with a different value type such as a long or a user-defined structure as its parameter is created at another point in your code. As a result, the runtime generates another version of the generic type and substitutes a long in the appropriate locations in MSIL. Conversions are no longer necessary because each specialized generic class natively contains the value type.

Generics work somewhat differently for reference types. The first time a generic type is constructed with any reference type, the runtime creates a specialized generic type with object references substituted for the parameters in the MSIL. Then, every time that a constructed type is instantiated with a reference type as its parameter, regardless of what type it is, the runtime reuses the previously created specialized version of the generic type. This is possible because all references are the same size.

For example, suppose you had two reference types, a Customer class and an Order class, and also suppose that you created a stack of Customer types:

C#
class Customer { }
class Order { }
C#
Stack<Customer> customers;

At this point, the runtime generates a specialized version of the Stack<T> class that stores object references that will be filled in later instead of storing data. Suppose the next line of code creates a stack of another reference type, which is named Order:

C#
Stack<Order> orders = new Stack<Order>();

Unlike with value types, another specialized version of the Stack<T> class is not created for the Order type. Instead, an instance of the specialized version of the Stack<T> class is created and the orders variable is set to reference it. Suppose that you then encountered a line of code to create a stack of a Customer type:

C#
customers = new Stack<Customer>();

As with the previous use of the Stack<T> class created by using the Order type, another instance of the specialized Stack<T> class is created. The pointers that are contained therein are set to reference an area of memory the size of a Customer type. Because the number of reference types can vary wildly from program to program, the C# implementation of generics greatly reduces the amount of code by reducing to one the number of specialized classes created by the compiler for generic classes of reference types.

Moreover, when a generic C# class is instantiated by using a value type or reference type parameter, reflection can query it at runtime and both its actual type and its type parameter can be ascertained.






Generics in the .NET Framework Class Library


Version 2.0 of the .NET Framework class library provides a new namespace, System.Collections.Generic, which includes several ready-to-use generic collection classes and associated interfaces. Other namespaces, such as System, also provide new generic interfaces such as IComparable<T>. These classes and interfaces are more efficient and type-safe than the non-generic collection classes provided in earlier releases of the .NET Framework. Before designing and implementing your own custom collection classes, consider whether you can use or derive a class from one of the classes provided in the .NET Framework class library.






Generics and Reflection


Because the Common Language Runtime (CLR) has access to generic type information at run time, you can use reflection to obtain information about generic types in the same way as for non-generic types. For more information, see Generics in the Run Time.

In the .NET Framework 2.0 several new members are added to the Type class to enable run-time information for generic types. See the documentation on these classes for more information on how to use these methods and properties. The System.Reflection.Emit namespace also contains new members that support generics. See How to: Define a Generic Type with Reflection Emit.

For a list of the invariant conditions for terms used in generic reflection, see the IsGenericType property remarks.

System.Type Member NameDescription
IsGenericTypeReturns true if a type is generic.
GetGenericArgumentsReturns an array of Type objects that represent the type arguments supplied for a constructed type, or the type parameters of a generic type definition.
GetGenericTypeDefinitionReturns the underlying generic type definition for the current constructed type.
GetGenericParameterConstraintsReturns an array of Type objects that represent the constraints on the current generic type parameter.
ContainsGenericParametersReturns true if the type or any of its enclosing types or methods contain type parameters for which specific types have not been supplied.
GenericParameterAttributesGets a combination of GenericParameterAttributes flags that describe the special constraints of the current generic type parameter.
GenericParameterPositionFor a Type object that represents a type parameter, gets the position of the type parameter in the type parameter list of the generic type definition or generic method definition that declared the type parameter.
IsGenericParameterGets a value that indicates whether the current Type represents a type parameter of a generic type or method definition.
IsGenericTypeDefinitionGets a value that indicates whether the current Type represents a generic type definition, from which other generic types can be constructed. Returns true if the type represents the definition of a generic type.
DeclaringMethodReturns the generic method that defined the current generic type parameter, or null if the type parameter was not defined by a generic method.
MakeGenericTypeSubstitutes the elements of an array of types for the type parameters of the current generic type definition, and returns a Type object representing the resulting constructed type.

In addition, new members are added to the MethodInfo class to enable run-time information for generic methods. See the IsGenericMethod property remarks for a list of invariant conditions for terms used to reflect on generic methods.+

System.Reflection.MemberInfo Member NameDescription
IsGenericMethodReturns true if a method is generic.
GetGenericArgumentsReturns an array of Type objects that represent the type arguments of a constructed generic method or the type parameters of a generic method definition.
GetGenericMethodDefinitionReturns the underlying generic method definition for the current constructed method.
ContainsGenericParametersReturns true if the method or any of its enclosing types contain any type parameters for which specific types have not been supplied.
IsGenericMethodDefinitionReturns true if the current MethodInfo represents the definition of a generic method.
MakeGenericMethodSubstitutes the elements of an array of types for the type parameters of the current generic method definition, and returns a MethodInfo object representing the resulting constructed method.






Generics and Attributes


Attributes can be applied to generic types in the same way as non-generic types. For more information on applying attributes, see Attributes.

Custom attributes are only permitted to reference open generic types, which are generic types for which no type arguments are supplied, and closed constructed generic types, which supply arguments for all type parameters.

The following examples use this custom attribute:

C#
class CustomAttribute : System.Attribute
{
    public System.Object info;
}

An attribute can reference an open generic type:

C#
public class GenericClass1<T> { }

[CustomAttribute(info = typeof(GenericClass1<>))]
class ClassA { }

Specify multiple type parameters using the appropriate number of commas. In this example, GenericClass2 has two type parameters:

C#
public class GenericClass2<T, U> { }

[CustomAttribute(info = typeof(GenericClass2<,>))]
class ClassB { }

An attribute can reference a closed constructed generic type:

C#
public class GenericClass3<T, U, V> { }

[CustomAttribute(info = typeof(GenericClass3<int, double, string>))]
class ClassC { }

An attribute that references a generic type parameter will cause a compile-time error:

C#
//[CustomAttribute(info = typeof(GenericClass3<int, T, string>))]  //Error
class ClassD<T> { }

A generic type cannot inherit from Attribute:

C#
//public class CustomAtt<T> : System.Attribute {}  //Error

To obtain information about a generic type or type parameter at run time, you can use the methods of System.Reflection. For more information, see Generics and Reflection







:
Posted by 지훈2
2017. 10. 6. 20:57

Programming guide - Indexers 프로그래밍/C#2017. 10. 6. 20:57


  1. Indexers
  2. Using Indexers
  3. Indexers in Interfaces
  4. Comparison Between Properties and Indexers





https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/indexers/




Indexers


Indexers allow instances of a class or struct to be indexed just like arrays. The indexed value can be set or retrieved without explicitly specifying a type or instance member. Indexers resemble properties except that their accessors take parameters.

The following example defines a generic class with simple get and set accessor methods to assign and retrieve values. The Program class creates an instance of this class for storing strings.

C#
using System;

class SampleCollection<T>
{
   // Declare an array to store the data elements.
   private T[] arr = new T[100];

   // Define the indexer to allow client code to use [] notation.
   public T this[int i]
   {
      get { return arr[i]; }
      set { arr[i] = value; }
   }
}

class Program
{
   static void Main()
   {
      var stringCollection = new SampleCollection<string>();
      stringCollection[0] = "Hello, World";
      Console.WriteLine(stringCollection[0]);
   }
}
// The example displays the following output:
//       Hello, World.
Note

For more examples, see Related Sections.

Expression Body Definitions

It is common for an indexer's get or set accessor to consist of a single statement that either returns or sets a value. Expression-bodied members provide a simplified syntax to support this scenario. Starting with C# 6, a read-only indexer can be implemented as an expression-bodied member, as the following example shows.

C#
using System;

class SampleCollection<T>
{
   // Declare an array to store the data elements.
   private T[] arr = new T[100];
   int nextIndex = 0;
   
   // Define the indexer to allow client code to use [] notation.
   public T this[int i] => arr[i];
   
   public void Add(T value)
   {
      if (nextIndex >= arr.Length) 
         throw new IndexOutOfRangeException($"The collection can hold only {arr.Length} elements.");
      arr[nextIndex++] = value;
   }
}

class Program
{
   static void Main()
   {
      var stringCollection = new SampleCollection<string>();
      stringCollection.Add("Hello, World");
      System.Console.WriteLine(stringCollection[0]);
   }
}
// The example displays the following output:
//       Hello, World.

Note that => introduces the expression body, and that the get keyword is not used.

Starting with C# 7, both the get and set accessor can be an implemented as expression-bodied members. In this case, both get and set keywords must be used. For example:

C#
using System;

class SampleCollection<T>
{
   // Declare an array to store the data elements.
   private T[] arr = new T[100];

   // Define the indexer to allow client code to use [] notation.
   public T this[int i]
   {
      get => arr[i]; 
      set => arr[i] = value; 
   }
}

class Program
{
   static void Main()
   {
      var stringCollection = new SampleCollection<string>();
      stringCollection[0] = "Hello, World.";
      Console.WriteLine(stringCollection[0]);
   }
}
// The example displays the following output:
//       Hello, World.

Indexers Overview

  • Indexers enable objects to be indexed in a similar manner to arrays.

  • A get accessor returns a value. A set accessor assigns a value.

  • The this keyword is used to define the indexer.

  • The value keyword is used to define the value being assigned by the set indexer.

  • Indexers do not have to be indexed by an integer value; it is up to you how to define the specific look-up mechanism.

  • Indexers can be overloaded.

  • Indexers can have more than one formal parameter, for example, when accessing a two-dimensional array.

Related Sections





Using Indexers


Indexers are a syntactic convenience that enable you to create a classstruct, or interface that client applications can access just as an array. Indexers are most frequently implemented in types whose primary purpose is to encapsulate an internal collection or array. For example, suppose you have a class named TempRecord that represents the temperature in Farenheit as recorded at 10 different times during a 24 hour period. The class contains an array named "temps" of type float to represent the temperatures, and a DateTime that represents the date the temperatures were recorded. By implementing an indexer in this class, clients can access the temperatures in a TempRecord instance as float temp = tr[4] instead of as float temp = tr.temps[4]. The indexer notation not only simplifies the syntax for client applications; it also makes the class and its purpose more intuitive for other developers to understand.

To declare an indexer on a class or struct, use the this keyword, as in this example:

public int this[int index]    // Indexer declaration  
{  
    // get and set accessors  
}  

Remarks

The type of an indexer and the type of its parameters must be at least as accessible as the indexer itself. For more information about accessibility levels, see Access Modifiers.

For more information about how to use indexers with an interface, see Interface Indexers.

The signature of an indexer consists of the number and types of its formal parameters. It does not include the indexer type or the names of the formal parameters. If you declare more than one indexer in the same class, they must have different signatures.

An indexer value is not classified as a variable; therefore, you cannot pass an indexer value as a ref or outparameter.

To provide the indexer with a name that other languages can use, use a name attribute in the declaration. For example:

[System.Runtime.CompilerServices.IndexerName("TheItem")]  
public int this [int index]   // Indexer declaration  
{  
}  

This indexer will have the name TheItem. Not providing the name attribute would make Item the default name.

Example 1

Description

The following example shows how to declare a private array field, temps, and an indexer. The indexer enables direct access to the instance tempRecord[i]. The alternative to using the indexer is to declare the array as a publicmember and access its members, tempRecord.temps[i], directly.

Notice that when an indexer's access is evaluated, for example, in a Console.Write statement, the get accessor is invoked. Therefore, if no get accessor exists, a compile-time error occurs.

Code

C#
class TempRecord
{
    // Array of temperature values
    private float[] temps = new float[10] { 56.2F, 56.7F, 56.5F, 56.9F, 58.8F, 
                                            61.3F, 65.9F, 62.1F, 59.2F, 57.5F };

    // To enable client code to validate input 
    // when accessing your indexer.
    public int Length
    {
        get { return temps.Length; }
    }
    // Indexer declaration.
    // If index is out of range, the temps array will throw the exception.
    public float this[int index]
    {
        get
        {
            return temps[index];
        }

        set
        {
            temps[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        TempRecord tempRecord = new TempRecord();
        // Use the indexer's set accessor
        tempRecord[3] = 58.3F;
        tempRecord[5] = 60.1F;

        // Use the indexer's get accessor
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, tempRecord[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();

    }
}
/* Output:
        Element #0 = 56.2
        Element #1 = 56.7
        Element #2 = 56.5
        Element #3 = 58.3
        Element #4 = 58.8
        Element #5 = 60.1
        Element #6 = 65.9
        Element #7 = 62.1
        Element #8 = 59.2
        Element #9 = 57.5
    */

Indexing Using Other Values

C# does not limit the index type to integer. For example, it may be useful to use a string with an indexer. Such an indexer might be implemented by searching for the string in the collection, and returning the appropriate value. As accessors can be overloaded, the string and integer versions can co-exist.

Example 2

Description

In this example, a class is declared that stores the days of the week. A get accessor is declared that takes a string, the name of a day, and returns the corresponding integer. For example, Sunday will return 0, Monday will return 1, and so on.

Code

C#
// Using a string as an indexer value
class DayCollection
{
    string[] days = { "Sun", "Mon", "Tues", "Wed", "Thurs", "Fri", "Sat" };

    // This method finds the day or returns -1
    private int GetDay(string testDay)
    {

        for (int j = 0; j < days.Length; j++)
        {
            if (days[j] == testDay)
            {
                return j;
            }
        }

        throw new System.ArgumentOutOfRangeException(testDay, "testDay must be in the form \"Sun\", \"Mon\", etc");
    }

    // The get accessor returns an integer for a given string
    public int this[string day]
    {
        get
        {
            return (GetDay(day));
        }
    }
}

class Program
{
    static void Main(string[] args)
    {
        DayCollection week = new DayCollection();
        System.Console.WriteLine(week["Fri"]);

        // Raises ArgumentOutOfRangeException
        System.Console.WriteLine(week["Made-up Day"]);

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
// Output: 5

Robust Programming

There are two main ways in which the security and reliability of indexers can be improved:

  • Be sure to incorporate some type of error-handling strategy to handle the chance of client code passing in an invalid index value. In the first example earlier in this topic, the TempRecord class provides a Length property that enables the client code to verify the input before passing it to the indexer. You can also put the error handling code inside the indexer itself. Be sure to document for users any exceptions that you throw inside an indexer accessor.

  • Set the accessibility of the get and set accessors to be as restrictive as is reasonable. This is important for the set accessor in particular. For more information, see Restricting Accessor Accessibility.





Indexers in Interfaces


Indexers can be declared on an interface. Accessors of interface indexers differ from the accessors of classindexers in the following ways:

  • Interface accessors do not use modifiers.

  • An interface accessor does not have a body.

Thus, the purpose of the accessor is to indicate whether the indexer is read-write, read-only, or write-only.

The following is an example of an interface indexer accessor:

C#
public interface ISomeInterface
{
    //...

    // Indexer declaration:
    string this[int index]
    {
        get;
        set;
    }
}

The signature of an indexer must differ from the signatures of all other indexers declared in the same interface.

Example

The following example shows how to implement interface indexers.

C#
// Indexer on an interface:
public interface ISomeInterface
{
    // Indexer declaration:
    int this[int index]
    {
        get;
        set;
    }
}

// Implementing the interface.
class IndexerClass : ISomeInterface
{
    private int[] arr = new int[100];
    public int this[int index]   // indexer declaration
    {
        get
        {
            // The arr object will throw IndexOutOfRange exception.
            return arr[index];
        }
        set
        {
            arr[index] = value;
        }
    }
}

class MainClass
{
    static void Main()
    {
        IndexerClass test = new IndexerClass();
        System.Random rand = new System.Random();
        // Call the indexer to initialize its elements.
        for (int i = 0; i < 10; i++)
        {
            test[i] = rand.Next();
        }
        for (int i = 0; i < 10; i++)
        {
            System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
        }

        // Keep the console window open in debug mode.
        System.Console.WriteLine("Press any key to exit.");
        System.Console.ReadKey();
    }
}
/* Sample output:
    Element #0 = 360877544
    Element #1 = 327058047
    Element #2 = 1913480832
    Element #3 = 1519039937
    Element #4 = 601472233
    Element #5 = 323352310
    Element #6 = 1422639981
    Element #7 = 1797892494
    Element #8 = 875761049
    Element #9 = 393083859
 */

In the preceding example, you could use the explicit interface member implementation by using the fully qualified name of the interface member. For example:

public string ISomeInterface.this   
{   
}   

However, the fully qualified name is only needed to avoid ambiguity when the class is implementing more than one interface with the same indexer signature. For example, if an Employee class is implementing two interfaces, ICitizen and IEmployee, and both interfaces have the same indexer signature, the explicit interface member implementation is necessary. That is, the following indexer declaration:

public string IEmployee.this   
{   
}   

implements the indexer on the IEmployee interface, while the following declaration:

public string ICitizen.this   
{   
}   

implements the indexer on the ICitizen interface.





Comparison Between Properties and Indexers


Indexers are like properties. Except for the differences shown in the following table, all the rules that are defined for property accessors apply to indexer accessors also.

PropertyIndexer
Allows methods to be called as if they were public data members.Allows elements of an internal collection of an object to be accessed by using array notation on the object itself.
Accessed through a simple name.Accessed through an index.
Can be a static or an instance member.Must be an instance member.
get accessor of a property has no parameters.get accessor of an indexer has the same formal parameter list as the indexer.
set accessor of a property contains the implicit value parameter.set accessor of an indexer has the same formal parameter list as the indexer, and also to the value parameter.
Supports shortened syntax with Auto-Implemented Properties.Does not support shortened syntax.











:
Posted by 지훈2