Hadoop is optimized for streaming large amounts of data and processing the data as a stream. Hadoop doesn't require a set data structure, the structure may be imposed at read time. That said, there are helper classes that allow you to write in various record-oriented formats.
Hadoop is said to be high-latency because when you run a MapReduce job, there's a bit of overhead for the job to start up and run. It is also optimized for volume--it is fast when you want to load a huge amount of data from disk, albeit not instantaneously. Not to mention, if you want to read a specific record, you will have to scan through potentially the entire data set to get your result.
HBase lives on top of Hadoop and reads/writes records in a custom format. HBase is a column-oriented Key/Value store, where keys are sorted in a b-tree for fast retrieval. HBase allows for low-latency random reads and writes, and it achieves this largely by caching data operations in memory.
(source: http://getsatisfaction.com/cloudera/topics/difference_between_hbase_and_hadoop_structure_i_e_both_denormalized_already)
Thursday, November 18, 2010
Database Normalization
Consider a org chart that has to be represented in a database
1 NF
Table A
Column Header > Person ID | Person Name | Report ID 1 | Report ID 2 | Report ID 3
Row > 23 Jack 24 14 10
Here, multiple manager-report relationships are represented in one row.
In 1NF, each relationship is represented in it's own row.
The new structure would look like:
Table A
1 NF
Table A
Column Header > Person ID | Person Name | Report ID 1 | Report ID 2 | Report ID 3
Row > 23 Jack 24 14 10
Here, multiple manager-report relationships are represented in one row.
In 1NF, each relationship is represented in it's own row.
The new structure would look like:
Table A
Column Header > Person ID | Person Name
Row > 23 Jack
Table B
Column Header > Person ID | Report ID |
Row > 23 24
Row > 23 14
Row > 23 10
2 NF
2 NF
Consider we are storing addess for each Person in Table A
Table A
Column Header > Person Id | Person Name | Address | City | State | Zip
Row 1 Jack 10 R rd NYC NY 100001
Row 2 Jill 12 Z Rd NYC NY 100001
The last 3 columns in the two rows contain duplicate information and can be converted into its on data unit.
Table C
Column Header Place Id | City | State | Zip
Row 1 NYC NY 100001
Table A
3 NF
Consider
Orders Table
Column Header | Order Id | UnitPrice | Quantity | Total
Row 1 10 2 20
4 NF
Consider a data struct (A, B, C)
A to B is a 1:many relationship.
A to C is a 1:many relationship.
B and C have no relationship between them.
Storying (A,B,C) in a table would give us composite key uniqueness, but would involve more keys than necessary.
e.g
A takes values (1,2,3)
B takes values (x,y,z)
C takes values (P,Q,R)
(A,B) have an explicit relationship where only certain pairs are possible.
(A,C) have an explicit relationship where only certain pairs are possible.
(B,C) have an implicit relationship. Any pair is possible.
We can leverage the fact that there is an implicit relationship, to reduce the number of rows. The implicit relationship does not need to be represented.
Each explicit relationship can be expressed in its own table.
We'll now have upto 3 * 3 * 3 = 27 rows (Table 1), when the same information can be represented in upto 18 rows over two tables.(Tables 2, 3)
Table 1 A B C (27 rows)
1 x P
1 x Q
.....
3 z R
Table 2 A B (upto 9 rows)
1 x
...
3 z
Table 3 A C (upto 9 rows)
1 P
...
3 R
4NF involves exposing multiple relationships in the same unit of data.
Here the reduction is possible only because there is no relationship between B, C.
Otherwise 4 NF would not have been possible. e.g. if the (B,C) combination of (y, Q) is not possible, it implies that B,C are coupled and we would probably have to see if 5 NF could be applied.
Check out http://en.wikipedia.org/wiki/Fourth_normal_form#Example for a better 4NF example.
A unit of data should be one, where any two fields cannot be expressed as a seperate unit of data. A relationship can only be formed between independent units of data.
5NF
Consider a table with 3 columns, A, B, C
Assume that (A,B), (B,C), (A,C) are relationships that are independent of each other.
Then, we can break the table into 3 tables, leading to lesser number of rows.
We break (A,B,C) into (A,B), (B,C) and (A,C)
If the relationships were not independent of each other, 5 NF would not have been possible.
Check out http://en.wikipedia.org/wiki/Fifth_normal_form#Example for a great example.
Table A
Column Header > Person Id | Person Name | Address | City | State | Zip
Row 1 Jack 10 R rd NYC NY 100001
Row 2 Jill 12 Z Rd NYC NY 100001
The last 3 columns in the two rows contain duplicate information and can be converted into its on data unit.
Table C
Column Header Place Id | City | State | Zip
Row 1 NYC NY 100001
Table A
Column Header > Person Id | Person Name | Address | Place Id
Row 1 Jack 10 R rd 1
Row 2 Jill 12 Z Rd 1
2NF involves exposing a hidden relationship within the same unit of data. In the example above, data about a person (name, address, city, state, zip) could be split in two two units of data with a relationship i.e. Person (name, address, Place Id) and Place (City, State, Zip)
In general, normalization improves (update/delete/add) efficiency, but reduces read efficiency. For data, which is not updated frequently (thus not requiring write efficiency), we might want to keep the data in denormalized form, instead of splitting in over multiple tables. Data redundancy increases, storage requirement increases, data modifications are more complex and need to be made in several places/rows/tables.
In general, normalization improves (update/delete/add) efficiency, but reduces read efficiency. For data, which is not updated frequently (thus not requiring write efficiency), we might want to keep the data in denormalized form, instead of splitting in over multiple tables. Data redundancy increases, storage requirement increases, data modifications are more complex and need to be made in several places/rows/tables.
3 NF
Consider
Orders Table
Column Header | Order Id | UnitPrice | Quantity | Total
Row 1 10 2 20
Row 2 5 6 30
This table can be reduced to
This table can be reduced to
Column Header | Order Id | UnitPrice | Quantity
Row 1 10 2
Row 2 5 6
Our Data (Unit Price, Quantity, Total) has an implicit relationship Total = Unit Price * Quantity. Our data can thus be reduced to (Unit Price, Quantity). Since the relationship is implicit, we do not need to store the relationship in some second table (unlike 1NF, 2NF).
4 NF
Consider a data struct (A, B, C)
A to B is a 1:many relationship.
A to C is a 1:many relationship.
B and C have no relationship between them.
Storying (A,B,C) in a table would give us composite key uniqueness, but would involve more keys than necessary.
e.g
A takes values (1,2,3)
B takes values (x,y,z)
C takes values (P,Q,R)
(A,B) have an explicit relationship where only certain pairs are possible.
(A,C) have an explicit relationship where only certain pairs are possible.
(B,C) have an implicit relationship. Any pair is possible.
We can leverage the fact that there is an implicit relationship, to reduce the number of rows. The implicit relationship does not need to be represented.
Each explicit relationship can be expressed in its own table.
We'll now have upto 3 * 3 * 3 = 27 rows (Table 1), when the same information can be represented in upto 18 rows over two tables.(Tables 2, 3)
Table 1 A B C (27 rows)
1 x P
1 x Q
.....
3 z R
Table 2 A B (upto 9 rows)
1 x
...
3 z
Table 3 A C (upto 9 rows)
1 P
...
3 R
4NF involves exposing multiple relationships in the same unit of data.
Here the reduction is possible only because there is no relationship between B, C.
Otherwise 4 NF would not have been possible. e.g. if the (B,C) combination of (y, Q) is not possible, it implies that B,C are coupled and we would probably have to see if 5 NF could be applied.
Check out http://en.wikipedia.org/wiki/Fourth_normal_form#Example for a better 4NF example.
A unit of data should be one, where any two fields cannot be expressed as a seperate unit of data. A relationship can only be formed between independent units of data.
5NF
Consider a table with 3 columns, A, B, C
Assume that (A,B), (B,C), (A,C) are relationships that are independent of each other.
Then, we can break the table into 3 tables, leading to lesser number of rows.
We break (A,B,C) into (A,B), (B,C) and (A,C)
If the relationships were not independent of each other, 5 NF would not have been possible.
Check out http://en.wikipedia.org/wiki/Fifth_normal_form#Example for a great example.
Wednesday, November 17, 2010
Lock-Free Concurrent Stack
package self.concurrent;
import java.util.concurrent.atomic.AtomicReference;
public class Stack {
AtomicReference> atomicHead;
public Stack()
{
atomicHead = new AtomicReference>();
}
public void push(T item)
{
Node currentHead = null;
Node newHead = null;
do
{
currentHead = atomicHead.get();
newHead = new Node(item, currentHead);
}
while(!atomicHead.compareAndSet(currentHead, newHead));
}
public T pop()
{
Node currentHead = null;
Node newHead = null;
do
{
currentHead = atomicHead.get();
if (currentHead == null) return null;
newHead = currentHead.next;
}
while(!atomicHead.compareAndSet(currentHead, newHead));
return currentHead.item;
}
}
import java.util.concurrent.atomic.AtomicReference;
public class Stack
AtomicReference
public Stack()
{
atomicHead = new AtomicReference
}
public void push(T item)
{
Node
Node
do
{
currentHead = atomicHead.get();
newHead = new Node
}
while(!atomicHead.compareAndSet(currentHead, newHead));
}
public T pop()
{
Node
Node
do
{
currentHead = atomicHead.get();
if (currentHead == null) return null;
newHead = currentHead.next;
}
while(!atomicHead.compareAndSet(currentHead, newHead));
return currentHead.item;
}
}
ConcurrentQueue using Lock-Conditions
package self.synchronization;
import java.util.concurrent.locks.*;
public class BlockingQueue3
{
private Node head = null;
private Node tail = null;
private final Lock lock = new ReentrantLock();
private final Condition itemAvailableCondition = lock.newCondition();
private final Condition spaceAvailableCondition = lock.newCondition();
private final int maxCount = 10; // make this a constructor param
private volatile int currentCount = 0;
public void produce(T item) throws InterruptedException {
Node newTail = new Node(item, null);
try
{
this.lock.lock();
// condition
while (this.currentCount >= maxCount) {
spaceAvailableCondition.await();
}
if (tail != null) {
tail.next = newTail;
} else {
tail = newTail;
head = newTail;
}
++this.currentCount;
this.itemAvailableCondition.signal();
}
finally
{
this.lock.unlock();
}
}
public T consume() throws InterruptedException {
Node returnNode = null;
try
{
this.lock.lock();
// condition
while (currentCount == 0) {
this.itemAvailableCondition.await();
}
returnNode = head;
head = head.next;
if (head == null)
tail = null;
--currentCount;
this.spaceAvailableCondition.signal();
}
finally
{
this.lock.unlock();
}
return returnNode.item;
}
private class Node {
public final T item;
public Node next;
Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
import java.util.concurrent.locks.*;
public class BlockingQueue3
{
private Node head = null;
private Node tail = null;
private final Lock lock = new ReentrantLock();
private final Condition itemAvailableCondition = lock.newCondition();
private final Condition spaceAvailableCondition = lock.newCondition();
private final int maxCount = 10; // make this a constructor param
private volatile int currentCount = 0;
public void produce(T item) throws InterruptedException {
Node newTail = new Node(item, null);
try
{
this.lock.lock();
// condition
while (this.currentCount >= maxCount) {
spaceAvailableCondition.await();
}
if (tail != null) {
tail.next = newTail;
} else {
tail = newTail;
head = newTail;
}
++this.currentCount;
this.itemAvailableCondition.signal();
}
finally
{
this.lock.unlock();
}
}
public T consume() throws InterruptedException {
Node returnNode = null;
try
{
this.lock.lock();
// condition
while (currentCount == 0) {
this.itemAvailableCondition.await();
}
returnNode = head;
head = head.next;
if (head == null)
tail = null;
--currentCount;
this.spaceAvailableCondition.signal();
}
finally
{
this.lock.unlock();
}
return returnNode.item;
}
private class Node {
public final T item;
public Node next;
Node(T item, Node next) {
this.item = item;
this.next = next;
}
}
}
Monday, November 15, 2010
What's the number again
The previous post "What's The Number" had a non-recursive implementation. Here is a recursive way of doing the same. It's not as bad to have a recursive implementation as the max recursion depth is limited by the length of a telephone number. If the input was longer / without an upper bound, recursion would be a bad move. This Recursive algorithm does a Depth First walk. A recursive Breadth First walk is much more painful to implement and has a higher memory overhead.
/*
Given a telephone number like 4156434531, get all possible names that would match it.
e.g. If the number is 23, the names could be "ad", "bd", "cd", "ae" etc..
*/
public static class TelNumber
{
private static char[][] KeyChars = new char[10][];
static TelNumber()
{
KeyChars[0] = new char[]{'+'};
KeyChars[1] = new char[]{' '};
KeyChars[2] = new char[]{'a','b','c'};
KeyChars[3] = new char[]{'d','e','f'};
KeyChars[4] = new char[]{'g','h','i'};
..
}
public static void PrintCombination(StringBuffer prefix, int[] tel_no)
{
int index = prefix.Count;
if (index >= tel_no.Length)
{
System.Console.Writeline(prefix);
return;
}
foreach(char c in KeyChars[tel_no[index]])
{
prefix.Add(c);
PrintCombination(prefix, tel_no);
prefix.RemoveLastChar();
}
}
What's the number
/*
Given a telephone number like 4156434531, get all possible names that would match it.
e.g. If the number is 23, the names could be "ad", "bd", "cd", "ae" etc..
*/
This non recursive implementation does not use a queue/stack to simulate recursion either.
namespace AlphaNumerics
{
public static class TelNumber
{
private static char[][] KeyChars = new char[10][];
static TelNumber()
{
KeyChars[0] = new char[]{'+'};
KeyChars[1] = new char[]{' '};
KeyChars[2] = new char[]{'a','b','c'};
KeyChars[3] = new char[]{'d','e','f'};
KeyChars[4] = new char[]{'g','h','i'};
}
public static void GetCombinations(int[] tel_no)
{
int[] digits = new int[tel_no.Length];
while(true)
{
Print(digits, tel_no);
if (!Increment(digits, tel_no)) break;
}
}
public static bool Increment(int[] digits, int[] tel_no)
{
int i = 0;
bool overflow = false;
for (; i < digits.Length; i++)
{
digits[i] = (digits[i] + 1) % KeyChars[tel_no[i]].Length;
if (digits[i] != 0) { overflow = false; break; }
else overflow = true;
}
return !overflow;
}
public static void Print(int[] digits, int[] tel_no)
{
for (int i = 0; i < tel_no.Length; i++)
{
System.Console.Write(KeyChars[tel_no[i]][digits[i]]);
}
System.Console.WriteLine();
}
}
}
Fuzzy Regex Matcher
/*
This Fuzzy RegexMatcher does not handle *,?, ^ etc., but, it takes in a tolerance/forgiveness factor.
With a forgiveness value of 1, regex "ac" would match strings like "abc" or "ad" or "c". With a forgiveness factor of 0, the match would fail.
*/
This Fuzzy RegexMatcher does not handle *,?, ^ etc., but, it takes in a tolerance/forgiveness factor.
With a forgiveness value of 1, regex "ac" would match strings like "abc" or "ad" or "c". With a forgiveness factor of 0, the match would fail.
*/
using System;
namespace StringHelpers
{
public static partial class RegexMatcher
{
public static bool AreSimilar(char[] str, char[] pattern, int forgiveness)
{
// Args check todo
for (int i = 0; i <= str.Length - pattern.Length; i++)
{
if (AreSimilar(str, pattern, i/*str index*/, 0/*pattern index*/, forgiveness)) return true;
}
return false;
}
private static bool AreSimilar(char[] str, char[] pattern, int si, int pi, int forgiveness)
{
if (pi >= pattern.Length) return true;
if (si >= str.Length) return false;
if (str[si] == pattern[pi])
{
return AreSimilar(str, pattern, si + 1, pi + 1, forgiveness);
}
else if (forgiveness > 0)
{
return AreSimilar(str, pattern, si, pi + 1, forgiveness - 1)|| AreSimilar(str, pattern, si + 1, pi + 1, forgiveness - 1);
}
return false;
}
}
}
Regex Matcher
/* Regex Pattern Matching
Handles ^, $ and *
*/
using System;
namespace StringHelper
{
public static partial class RegexMatcher
{
public static bool IsMatch(char[] str, char[] pattern)
{
if (pattern[0] == '^')
return IsMatch(str, 0, pattern, 1);
for (int i = 0; i < str.Length; i++)
{
if (IsMatch(str, i, pattern, 0)) return true;
}
return false;
}
// handle ? *
public static bool IsMatch(char[] str, int si, char[] pattern, int pi)
{
// pattern is over. IsMatch is true.
if (pattern == null || pi >= pattern.Length) return true;
if (pattern[pi] == '$')
{
if (si == str.Length) return true;
else return false;
}
// pattern is not over.
if (si >= str.Length) return false;
// pattern is not exhausted and str is not exhausted.
if (pattern[pi] == '?')
return IsMatch(str, si + 1, pattern, pi + 1) || IsMatch(str, si, pattern, pi + 1);
if (pattern[pi] == '*')
{
for (int i = si; i < str.Length; i++)
if (IsMatch(str, i, pattern, pi + 1)) return true;
}
if (pattern[pi] == str[si])
return IsMatch(str, si + 1, pattern, pi + 1);
return false;
}
}
}
Bit Reversal
Let's consider reversing a byte in a speedy way using some parallelism.
Byte b;
b = b & FO >> 4 | b & OF << 4; // This swaps the nibbles.
b = b & CC >> 2 | b & 33 << 2; // swap bit pairs in each nibble.
b = b & AA >> 1 | b & 55 << 1; // swap even / odd bits
Done!
Byte b;
b = b & FO >> 4 | b & OF << 4; // This swaps the nibbles.
b = b & CC >> 2 | b & 33 << 2; // swap bit pairs in each nibble.
b = b & AA >> 1 | b & 55 << 1; // swap even / odd bits
Done!
Non Recursive Tree Traversal
Tree traversal without recursion is slightly tricky using a stack especially if we want to do it without keeping track of visit state explicitly. Searching online, I could not find a good implementation. So I decided to write my own using stacks. If you are looking for a non-recursive, non-stack implementation, read up on Morris Traversal, which is based on Threaded Trees.
The following logic is largely derived from Donald Knuth's implementation in 'Art Of Computer Programming - 2.3.1'
In Order Traversal.
void PrintPreOrder(Node root)
{
Post Order Traversal
void PrintPostOrder(Node root)
{
The following logic is largely derived from Donald Knuth's implementation in 'Art Of Computer Programming - 2.3.1'
In Order Traversal.
void PrintInOrder(Node root)
{
{
Stack stack = new Stack();
Node p = root;
while (p != null)
{
Node p = root;
while (p != null)
{
stack.Push(p);
// Update p
p = p.Left;
// Update p
p = p.Left;
while (p == null && !stack.IsEmpty())
{
{
p = stack.Pop();
System.Console.WritelLine(p);
p = p.Right;
System.Console.WritelLine(p);
p = p.Right;
}
}
}
Pre Order Traversalvoid PrintPreOrder(Node root)
{
Stack stack = new Stack();
Node p = root;
while (p != null)
{
Node p = root;
while (p != null)
{
System.Console.Writeline(p);
if (p.Right != null) stack.Push(p.Right);
if (p.Right != null) stack.Push(p.Right);
// Update p
p = p.Left;
p = p.Left;
if (p == null && !stack.IsEmpty())
{
{
p = stack.Pop();
}
}
}Post Order Traversal
void PrintPostOrder(Node root)
{
Stack result = new Stack();
Stack stack = new Stack();
Node p = root;
while (p != null)
{
Stack stack = new Stack();
Node p = root;
while (p != null)
{
result.Push(p);
if (p.Left != null) stack.Push(p.Left);
p = p.Right;
if(p == null && !stack.IsEmpty())
{
if (p.Left != null) stack.Push(p.Left);
p = p.Right;
if(p == null && !stack.IsEmpty())
{
p = stack.Pop();
}
}
while(!result.IsEmpty())
{
{
System.Console.Writeline(result.Pop());
}
}Beautiful Asymmetric QuickSort
QuickSort can be tricky and atleast one of its implementations printed in a widely used book had in it a subtle bug.
Found an excellent concise implementation in the book "Programming Pearls" by John Bentley.
void quicksort(int left, int right){
if ( left <= right ) return;
int pivotIndex = rand(left, right);
int pivotValue = x[pivotIndex];
swap(x[left], x[pivotIndex]);
int pivotIndex = rand(left, right);
int pivotValue = x[pivotIndex];
swap(x[left], x[pivotIndex]);
int partitionIndex = left;
for (int i = left + 1 ; i <= right; i ++)if ( x[i] < pivotValue ) swap ( x[++partitionIndex], x[i]);
swap (x[left], x[partitionIndex]);
quicksort(left, partitionIndex -1);
quicksort(partitionIndex + 1, right);
quicksort(left, partitionIndex -1);
quicksort(partitionIndex + 1, right);
}
Lets see how we could build the above program:
Step 1: Need a find a partition point m. Given m, we need to recurse with quicksort(l, m -1); quicksort (m + 1, u);
Step 2: For recursive programs, an exit strategy is required. if ( l >= u) return;
Step 3: It is difficult to have the pivot value in exactly the middle of the array bcz:
1. We really want to select the pivot location randomly.
2. There is no guarantee that the pivot value will end up dividing the array in two equal subarrays.
[youtube=http://www.youtube.com/watch?v=aMnn0Jq0J-E]
As John B mentions in the video, humans are prone to symmetry potentially leading to complex code in the quicksort implementations. So the approach to take is : select a pivot randomly and move it to the first location of the array.
And beautiful code is born !
Synchronized Blocking Queue - Part I
Synchronization can be solved using intrinsic locks, explicit locking or optimistic locking with Compare-And-Swap (CAS) logic.
This implementation uses notifyAll() which can be avoided using Java's new Lock and Condition objects.
NotifyAll is needed for the implementation to work correctly but the down side is that more threads than necessary are signalled when state changes.
This post deals with a rudimentary in-efficient way of synchronization :
package self.synchronization;
public class BlockingQueue1 {
private Node head = null;
private Node tail = null;
private final int maxCount = 10;
private volatile int currentCount = 0;
public void produce(T item) throws InterruptedException
{
public T consume() throws InterruptedException
{
private class Node
{
}
This implementation uses notifyAll() which can be avoided using Java's new Lock and Condition objects.
NotifyAll is needed for the implementation to work correctly but the down side is that more threads than necessary are signalled when state changes.
This post deals with a rudimentary in-efficient way of synchronization :
package self.synchronization;
public class BlockingQueue1
private Node head = null;
private Node tail = null;
private final int maxCount = 10;
private volatile int currentCount = 0;
public void produce(T item) throws InterruptedException
{
Node newTail = new Node(item, null);
synchronized(this)
{
// condition
while (this.currentCount >= maxCount)
{
this.wait();
}
if (tail != null)
{
tail.next = newTail;
}
else
{
tail = newTail;
head = newTail;
}
++this.currentCount;
this.notifyAll();
}
}public T consume() throws InterruptedException
{
Node returnNode = null;
synchronized(this)
{
// condition
while (currentCount == 0)
{
this.wait();
}
returnNode = head;
head = head.next;
if (head == null) tail = null;
--currentCount;
this.notifyAll();
}
return returnNode.item;
}private class Node
{
public final T item;
public Node next;
Node(T item, Node next)
{
this.item = item;
this.next = next;
}
}}
What's new
C# new method modifierPurpose: If you want to redefine a method visible to a derived class, earlier defined in some base class in the hierarchy, do it with new.
False : new is only for redefining virtual methods.
new is also for redefining methods marked as virtual.
True : new hides the base implementation.
But it is possible to still access the implementation in base.
False : new cannot be used with static membersA static member in C# can't be marked as override, virtual or abstract. However it is possible to hide a base class static method in a derived class by using the keyword new.
The following example should help out:
class A { public string MyName() {return "A";}}
class B:A { new public virtual string MyName() {return "B";}}
class C: B {public override string MyName() {return "C";}}
class D:C {new public string MyName() {return "D";}}
A a = new B(); a.MyName() returns "A"
B b = new C(); b.MyName() returns "C";
D d = new D(); d.MyName() returns "D";
Note that polymorphic behavior is displayed by b. Virtual dispatch is used since 'virtual' is used.
But the polymorphic behavior is limited between B & C and does not extend to D, since in D, we have redefined the method.
False : new is only for redefining virtual methods.
new is also for redefining methods marked as virtual.
True : new hides the base implementation.
But it is possible to still access the implementation in base.
False : new cannot be used with static membersA static member in C# can't be marked as override, virtual or abstract. However it is possible to hide a base class static method in a derived class by using the keyword new.
The following example should help out:
class A { public string MyName() {return "A";}}
class B:A { new public virtual string MyName() {return "B";}}
class C: B {public override string MyName() {return "C";}}
class D:C {new public string MyName() {return "D";}}
A a = new B(); a.MyName() returns "A"
B b = new C(); b.MyName() returns "C";
D d = new D(); d.MyName() returns "D";
Note that polymorphic behavior is displayed by b. Virtual dispatch is used since 'virtual' is used.
But the polymorphic behavior is limited between B & C and does not extend to D, since in D, we have redefined the method.
Subscribe to:
Posts (Atom)